;;; ess-r-args.el --- Insert R function's arguments12;; Copyright (C) 2007 Sven Hartenstein <mail at svenhartenstein dot de>3;; Copyright (C) 2007 A.J. Rossini, Richard M. Heiberger, Martin4;; Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen.56;; This file is part of ESS78;; This file is free software; you can redistribute it and/or modify9;; it under the terms of the GNU General Public License as published by10;; the Free Software Foundation; either version 2, or (at your option)11;; any later version.1213;; This file is distributed in the hope that it will be useful,14;; but WITHOUT ANY WARRANTY; without even the implied warranty of15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16;; GNU General Public License for more details.1718;; A copy of the GNU General Public License is available at19;; http://www.r-project.org/Licenses/2021;; Last update: 2012-02-272223;;; Commentary:2425;; == DOCUMENTATION ==2627;; This file provides some functions that show or insert a28;; R-function's arguments (and their default values) by using R's29;; args() function. This code requires ESS (http://ess.r-project.org).30;; For screenshots as well as information on requirements,31;; installation, configuration and troubleshooting, please visit32;; http://www.svenhartenstein.de/emacs-ess.php3334;; Users of XEmacs (or maybe non-GNU-Emacs users): The code below must35;; be slightly adapted in order to work in XEmacs (i.e. comment out36;; the delete-trailing-whitespace function call by putting a semicolon37;; at the beginning of the line). Furthermore, the tooltip option does38;; NOT work in XEmacs (yet) (and will probably only ever work if the39;; ESS-core team will adapt the code for XEmacs compatibility).4041;; == Requirements ==4243;; * ESS mode must be loaded and running.44;; * A R process must be running within ESS.45;; * For the tooltip option to work, Emacs must not run within a46;; terminal but (directly) under the X window system (in case of47;; GNU/Linux).48;; * The tooltip option currently requires GNU Emacs (i.e. not XEmacs49;; or a similar other derivate).5051;; == Installation ==5253;; To make Emacs aware of the functions defined in this file load it54;; into Emacs by including something like the following in your55;; ~/.emacs file (adapt "/PATH/TO"!).56;;57;; (add-hook 'ess-mode-hook (lambda () (load "/PATH/TO/ess-r-args.el")))58;;59;; This file is included with ESS; if you are reading this file as60;; part of the ESS distribution, then you do not need to add the above61;; line.6263;; == Configuration ==6465;; Configuration should be done by setting some variables in your66;; ~/.emacs file, the following is an example only, adjust it to your67;; needs.6869;; ;; ess-r-args-noargsmsg is printed, if no argument information70;; ;; could be found. You could set it to an empty string ("") for no71;; ;; message.72;; (setq ess-r-args-noargsmsg "No args found.")73;;74;; ;; ess-r-args-show-as determines how (where) the information is75;; ;; displayed. Set it to 'tooltip little tooltip windows or to76;; ;; 'message (the default) which will use the echo area at the bottom of77;; ;; your Emacs frame.78;; (setq ess-r-args-show-as nil)79;;80;; ;; ess-r-args-show-prefix is a string that is printed in front of81;; ;; the arguments list. The default is "ARGS: ".82;; (setq ess-r-args-show-prefix "ARGS: ")8384;; == Usage ==8586;; The functions should be called when point (text cursor) is between87;; two parentheses of a R function call (see screenshots above). It88;; will then (invisibly) query R for which arguments the respective89;; function knows as well as their default values and show or insert90;; the result.9192;; There are currently two functions: ess-r-args-show echoes the93;; arguments in the echo area or as tooltip, ess-r-args-insert prints94;; the arguments at point.9596;; In order to not having to type the whole function name each time97;; you want to see the arguments list, you most probably want to bind98;; the functions to a key which again should be done in your ~/.emacs99;; file. You can also let Emacs call the function each time you insert100;; an opening parenthesis ("(").101102;; -----> do this below103104;; Again, the following is an example only:105106;; bind ess-r-args-show to F2107;; (define-key ess-mode-map [f2] 'ess-r-args-show)108109;; bind ess-r-args-insert to F3110;; (define-key ess-mode-map [f3] 'ess-r-args-insert)111112;; == Setting the tooltip position ==113114;; Unfortunately (?), tooltips are by default shown at the mouse115;; pointer's position, which is not necessarily where you are looking116;; at. If you use Emacs' mouse-avoidance-mode with option "banish"117;; then the mouse pointer will automatically be put at the upper right118;; corner of the Emacs window so that you know where to look for the119;; tooltip. Emacs also allows for setting the tooltip position120;; relative to the upper left corner of your screen. If you know how121;; to let Emacs determine the point (text cursor) position in pixels122;; from the upper left corner of the screen, please let me know. It123;; would then be possible to show the tooltip near the point, which I124;; would consider preferably.125;; SJE: see code at bottom 2009-01-30...126127;; ;; Put mouse away when using keyboard128;; (mouse-avoidance-mode 'banish)129130;; ;; Set the tooltip position in absolute pixels from the upper left131;; ;; corner of the screen132;; (setq tooltip-frame-parameters133;; '((name . "tooltip")134;; (left . 20)135;; (top . 20)))136137;; == Changelog ==138139;; * 2007-04-03: The function should now do nothing (instead of140;; irritating cursor movement) if the current ESS process is141;; anything but R.142;; * 2007-04-05: Function names changed. Much more modular. GPLed. New143;; prefix configuration variable. Minor changes.144;; * 2007-04-30: Error handling added. Bugfix: Emacs used to lock up145;; when function was called within parentheses following the "#"146;; character (Thanks to John Bullock for his bug report!).147148;; == Troubleshooting ==149150;; Before sending reports of problems, please check the following.151152;; * Doublecheck the requirements section above.153;; * Please be sure you tried both the "tooltip" option and the "echo154;; area" option and tell me whether both failed or just one.155;; * Check whether it is a key binding problem. Run the function with156;; M-x ess-r-args-show RET. If it works but not the key binding, try157;; binding it to another key. Some window managers might not pass158;; the function key keystrokes to Emacs.159160;; If you encounter problems, please send me detailed bug reports.161;; Please also indicate the ESS version you are running and the Emacs162;; type (GNU vs. X) and operating system. I will do my best to help163;; but please be aware that I'm everything but an emacs lisp expert.164165;; == TODO ==166167;; These are things that I would like to see improved. Please let me168;; know if you know how it could be done.169170;; * As mentioned above, I would like to place the tooltip near the171;; point (text cursor) but I do not see how this could be done.172;; * Both the message in the echo area and the tooltip automatically173;; disappear as soon as a key is pressed. That is, you will need to174;; call the function again if you have entered the first175;; parameter(s) and wonder what additional parameters are possible.176;; I would prefer the information to be shown, say, five seconds or177;; so.178179;;; Code:180181(eval-and-compile182(require 'ess-custom))183184(eval-when-compile185(if ess-has-tooltip186(require 'tooltip))); for tooltip-show187188(require 'ess)189190(defun ess-r-args-current-function ()191"Returns the name of the R function assuming point is currently192within the argument list or nil if no possible function name is193found."194(save-excursion195(condition-case nil (up-list -1)196(error (message "Can't find opening parenthesis.")))197(let ((posend (point)))198(backward-sexp 1)199(let ((rfunname (buffer-substring-no-properties posend (point))))200(if (posix-string-match "^[a-zA-Z0-9_\.]+$" rfunname)201rfunname nil)))))202203(defun ess-r-args-get (&optional function trim)204"Returns string of arguments and their default values of R205function FUNCTION or nil if no possible function name206found. Calls ess-r-args-current-function if no argument given.207If TRIM is non-nill remove tabs and newlines and replace ' = '208with '=' (useful for display in minibuffer to avoid window and209buffer readjustments for multiline string)."210(if (null function)211(setq function (ess-r-args-current-function)))212(when (and function213(or ess-current-process-name214(interactive-p)))215(ess-force-buffer-current "R process to use: ")216;; ^^^^^^^^^^^^^^^ has own error handler217(cadr (ess-function-arguments function))218))219220;; (let ((ess-nuke-trailing-whitespace-p t)221;; (args))222;; (ess-command (format "try({fun<-\"%s\"; fundef<-paste(fun, '.default',sep='')223;; if(exists(fundef, mode = \"function\")) args(fundef) else args(fun)}, silent=F)\n" function)224;; (get-buffer-create "*ess-r-args-tmp*"))225;; (with-current-buffer "*ess-r-args-tmp*"226;; (goto-char (point-min))227;; (if (null (search-forward "function" 20 t))228;; (message ess-r-args-noargsmsg)229;; (goto-char (point-min))230;; (search-forward "(" nil t)231;; (delete-region (point-min) (point))232;; (goto-char (point-max))233;; (search-backward ")" nil t)234;; (delete-region (point) (point-max))235;; (ess-nuke-trailing-whitespace); should also work in Xemacs236;; (setq args (buffer-string))237;; (if trim238;; (replace-regexp-in-string " = " "="239;; (replace-regexp-in-string "[\n \t]+" " " args))240;; args)241;; )))))242243(defun ess-r-args-show (&optional function)244"Show arguments and their default values of R function. Calls245\\[ess-r-args-current-function] if called without argument."246(interactive "*")247(ess-message "(ess-r-args-show): start")248(if (null function)249(setq function (ess-r-args-current-function)))250(ess-message ".... function='%s'" function)251(when function252(let* ((tt (and (equal ess-r-args-show-as 'tooltip)253ess-has-tooltip))254(args (concat ess-r-args-show-prefix (ess-r-args-get function (not tt)))))255(ess-message "(ess-r-args-show): args='%s'" args)256(when args257(if (not tt)258(message args)259(require 'tooltip)260;; value of 30 in next call is just a guess,261;; should really be based262;; on something like pixel height of 1-2 vertical263;; lines of text264(ess-tooltip-show-at-point args 0 30))265))))266267(defun ess-r-args-auto-show ()268"Typically assigned to \"(\": If there's an ess-process, automatically show arguments269and their default values of an R function. Built on \\[ess-r-args-show]."270(interactive)271(insert "("); (skeleton-pair-insert-maybe nil)272(if (and (not eldoc-mode)273ess-local-process-name ; has a process and it must still be running274(ess-get-process ess-local-process-name))275(ess-r-args-show)))276277;; MM: I would strongly discourage use of the following:278;; it leads to clueless newbie-users who indeed279;; explicitly call a function with all its default arguments;280;; instead of only setting the required arguments281(defun ess-r-args-insert (&optional function)282"Insert arguments and their default values of function. Calls283ess-r-args-current-function if no argument given."284(interactive "*")285(if (null function)286(setq function (ess-r-args-current-function)))287(if function288(let ((args (ess-r-args-get function))289(pointpos (point)))290(insert args)291(goto-char pointpos))))292293294;; (defvar ess-r-object-tooltip-alist295;; '((numeric . "summary")296;; (integer . "summary")297;; (factor . "table")298;; (lm . "summary")299;; (other . "str"))300;; "List of (<class> . <R-function>) to be used in \\[ess-r-object-tooltip].301;; For example, when called while point is on a factor object, a table of that302;; factor will be shown in the tooltip.303;; The special key \"other\" in the alist defines which function to call when304;; the class is not mached in the alist. The default, str(), is a fairly useful305;; default for many, including data.frame and function objects.")306307;; From Erik Iversion, slightly modified,308;; http://www.sigmafield.org/2009/10/01/r-object-tooltips-in-ess/309;; (defun ess-r-object-tooltip ()310;; "Get info for object at point, and display it in a tooltip."311;; (interactive)312;; (let ((proc (ess-get-process))313;; (objname (current-word))314;; (curbuf (current-buffer))315;; (tmpbuf (get-buffer-create " *ess-r-object-tooltip*"))316;; bs)317;; (when objname318;; (ess-write-to-dribble-buffer319;; (format "ess-r-object-tooltip: objname='%s'\n" objname))320;; (ess-command (concat "class(" objname ")\n") tmpbuf nil nil nil proc)321;; (with-current-buffer tmpbuf322;; (goto-char (point-min))323;; ;; CARE: The following can only work in an English language locale!324;; ;; .lang. <- Sys.getenv("LANGUAGE"); Sys.setenv(LANGUAGE="en")325;; ;; .lc. <- Sys.getlocale("LC_MESSAGES"); Sys.setlocale("LC_MESSAGES","en_US.utf-8")326;; ;; and *afterward* Sys.setenv(LANGUAGE=.lang.); Sys.setlocale("LC_MESSAGES", .lc.)327;; ;; but that fails sometimes, e.g., on Windows328;; (unless (re-search-forward "\(object .* not found\)\|unexpected" nil t)329;; (re-search-forward "\"\\(.*\\)\"" nil t)330;; (let* ((objcls (match-string 1))331;; (myfun (or (cdr (assoc-string objcls ess-r-object-tooltip-alist))332;; (cdr (assoc 'other ess-r-object-tooltip-alist)))))333;; (ess-command (concat myfun "(" objname ")\n") tmpbuf nil nil nil proc))334;; (setq bs (buffer-string)))))335;; (if bs336;; (ess-tooltip-show-at-point bs 0 30))))337338;; Erik: my default key map339;;(define-key ess-mode-map "\C-c\C-g" 'ess-r-object-tooltip)340341;; On http://www.sigmafield.org/2009/10/01/r-object-tooltips-in-ess/342;; in the comments, "Charlie" recommended343344;; (custom-set-faces345;; '(tooltip ((t (:background "white" :foreground "blue" :foundry "fixed")))))346347348349(provide 'ess-r-args)350351;;; ess-r-args.el ends here352353354