Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
12 KiB

преди 5 години
  1. ;;; ess-rdired.el --- prototype object browser for R, looks like dired mode. -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2002--2019 A.J. Rossini, Richard M. Heiberger, Martin
  3. ;; Maechler, Kurt Hornik, Rodney Sparapani, Stephen Eglen, and J. Alexander Branham.
  4. ;; Author: Stephen Eglen <stephen@anc.ed.ac.uk>
  5. ;; Created: Thu 24 Oct 2002
  6. ;; Maintainer: ESS-core <ESS-core@r-project.org>
  7. ;; This file is part of ESS
  8. ;; This file is not part of GNU Emacs.
  9. ;; ess-rdired.el is free software; you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation; either version 2, or (at your option)
  12. ;; any later version.
  13. ;; ess-rdired.el is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;; A copy of the GNU General Public License is available at
  18. ;; https://www.r-project.org/Licenses/
  19. ;; This provides a dired-like buffer for R objects. Instead of
  20. ;; operating on files, we operate on R objects in the current
  21. ;; environment. Objects can be viewed, edited, deleted, plotted and
  22. ;; so on.
  23. ;;; Commentary:
  24. ;; Do "M-x R" to start an R session, then create a few variables:
  25. ;;
  26. ;; s <- sin(seq(from=0, to=8*pi, length=100))
  27. ;; x <- c(1, 4, 9)
  28. ;; y <- rnorm(20)
  29. ;; z <- TRUE
  30. ;; Then in Emacs, do "M-x ess-rdired" and you should see the following in
  31. ;; the buffer *R dired*:
  32. ;; Name Class Length Size
  33. ;; s numeric 100 848 bytes
  34. ;; x numeric 3 80 bytes
  35. ;; y numeric 20 208 bytes
  36. ;; z logical 1 56 bytes
  37. ;; Type "?" in the buffer to see the documentation. e.g. when the
  38. ;; cursor is on the line for `s', type 'p' to plot it, or `v' to view
  39. ;; its contents in a buffer. Then type 'd' to delete it.
  40. ;; How it works.
  41. ;; Most of the hard work is done by the R routine .rdired.objects(),
  42. ;; which, when called, produces the list of objects in a tidy format.
  43. ;; This function is stored within the Lisp variable `ess-rdired-objects'.
  44. ;; Todo - How to select alternative environments? Currently only
  45. ;; shows objects in the .GlobalEnv? See BrowseEnv() in 1.6.x for way
  46. ;; of browsing other environments.
  47. ;; Todo - problem with fix -- have to wait for fix() command to return
  48. ;; before *R* buffer can be used again. This can get stuck, umm. not
  49. ;; sure what is going wrong here. Maybe add a hook to the temp buffer
  50. ;; so that when buffer is killed, we send an instruction to R to
  51. ;; update the value of the variable to the contents of the buffer.
  52. ;; This way *R* doesn't have to wait.
  53. ;; Todo - small bug in .rdired.objects -- if we have a variable called
  54. ;; `my.x', its value is replaced by the value of my.x used in the
  55. ;; sapply() calls within .rdired.objects().
  56. ;;; Code:
  57. (require 'ess-inf)
  58. (eval-when-compile
  59. (require 'subr-x))
  60. (defvar ess-rdired-objects "local({.rdired.objects <- function(objs) {
  61. if (length(objs)==0) {
  62. \"No objects to view!\"
  63. } else {
  64. mode <- sapply(objs, function(my.x) {
  65. eval( parse( text=sprintf('data.class(get(\"%s\"))', my.x))) })
  66. length <- sapply(objs, function(my.x) {
  67. eval( parse( text=sprintf('length(get(\"%s\"))', my.x))) })
  68. size <- sapply(objs, function(my.x) {
  69. eval( parse( text=sprintf('format(object.size(get(\"%s\")), units=\"b\")', my.x))) })
  70. d <- data.frame(mode, length, size)
  71. var.names <- row.names(d)
  72. ## If any names contain spaces, we need to quote around them.
  73. quotes = rep('', length(var.names))
  74. spaces = grep(' ', var.names)
  75. if (any(spaces))
  76. quotes[spaces] <- '\"'
  77. var.names = paste(quotes, var.names, quotes, sep='')
  78. row.names(d) <- paste(' ', var.names, sep='')
  79. d
  80. }
  81. }; cat('\n'); print(.rdired.objects(ls(envir = .GlobalEnv)))})\n"
  82. "Function to call within R to print information on objects.
  83. The last line of this string should be the instruction to call
  84. the function which prints the output for rdired.")
  85. (defvar ess-rdired-buffer "*R dired*"
  86. "Name of buffer for displaying R objects.")
  87. (defvar ess-rdired-auto-update-timer nil
  88. "The timer object for auto updates.")
  89. (defcustom ess-rdired-auto-update-interval 5
  90. "Seconds between refreshes of the `ess-rdired' buffer."
  91. :type '(choice (const nil :tag "No auto updates") (integer :tag "Seconds"))
  92. :group 'ess-R
  93. :package-version '(ess . "19.04"))
  94. (defvar ess-rdired-mode-map
  95. (let ((map (make-sparse-keymap)))
  96. (define-key map "d" #'ess-rdired-delete)
  97. (define-key map "x" #'ess-rdired-delete)
  98. (define-key map "v" #'ess-rdired-view)
  99. (define-key map "V" #'ess-rdired-View)
  100. (define-key map "p" #'ess-rdired-plot)
  101. (define-key map "y" #'ess-rdired-type)
  102. (define-key map "\C-c\C-s" #'ess-rdired-switch-process)
  103. (define-key map "\C-c\C-y" #'ess-switch-to-ESS)
  104. (define-key map "\C-c\C-z" #'ess-switch-to-end-of-ESS)
  105. map))
  106. (define-derived-mode ess-rdired-mode tabulated-list-mode "Rdired"
  107. "Major mode for output from `ess-rdired'.
  108. `ess-rdired' provides a dired-like mode for R objects. It shows the
  109. list of current objects in the current environment, one-per-line. You
  110. can then examine these objects, plot them, and so on."
  111. :group 'ess-R
  112. (setq mode-name (concat "RDired " ess-local-process-name))
  113. (setq tabulated-list-format
  114. `[("Name" 18 t)
  115. ("Class" 10 t)
  116. ("Length" 10 ess-rdired--length-predicate)
  117. ("Size" 10 ess-rdired--size-predicate)])
  118. (add-hook 'tabulated-list-revert-hook #'ess-rdired-refresh nil t)
  119. (when (and (not ess-rdired-auto-update-timer)
  120. ess-rdired-auto-update-interval)
  121. (setq ess-rdired-auto-update-timer
  122. (run-at-time t ess-rdired-auto-update-interval #'ess-rdired-refresh)))
  123. (add-hook 'kill-buffer-hook #'ess-rdired-cancel-auto-update-timer nil t)
  124. (tabulated-list-init-header))
  125. ;;;###autoload
  126. (defun ess-rdired ()
  127. "Show R objects from the global environment in a separate buffer.
  128. You may interact with these objects, see `ess-rdired-mode' for
  129. details."
  130. (interactive)
  131. (unless (and (string= "R" ess-dialect)
  132. ess-local-process-name)
  133. (error "Not in an R buffer with attached process"))
  134. (let ((proc ess-local-process-name))
  135. (pop-to-buffer (get-buffer-create ess-rdired-buffer))
  136. (setq ess-local-process-name proc)
  137. (ess-rdired-mode)
  138. (ess-rdired-refresh)))
  139. (defun ess-rdired-refresh ()
  140. "Refresh the `ess-rdired' buffer."
  141. (let* ((buff (get-buffer-create ess-rdired-buffer))
  142. (proc-name (buffer-local-value 'ess-local-process-name buff))
  143. (proc (get-process proc-name))
  144. (out-buff (get-buffer-create " *ess-rdired-output*"))
  145. text)
  146. (when (and proc-name proc
  147. (not (process-get proc 'busy)))
  148. (ess-command ess-rdired-objects out-buff nil nil nil proc)
  149. (with-current-buffer out-buff
  150. (goto-char (point-min))
  151. ;; Delete two lines. One filled with +'s from R's prompt
  152. ;; printing, the other with the header info from the data.frame
  153. (delete-region (point-min) (1+ (point-at-eol 2)))
  154. (setq text (split-string (buffer-string) "\n" t "\n"))
  155. (erase-buffer))
  156. (with-current-buffer buff
  157. (setq tabulated-list-entries
  158. (mapcar #'ess-rdired--tabulated-list-entries text))
  159. (let ((entry (tabulated-list-get-id))
  160. (col (current-column)))
  161. (tabulated-list-print)
  162. (while (not (equal entry (tabulated-list-get-id)))
  163. (forward-line))
  164. (move-to-column col))))))
  165. (defun ess-rdired-cancel-auto-update-timer ()
  166. "Cancel the timer `ess-rdired-auto-update-timer'."
  167. (setq ess-rdired-auto-update-timer
  168. (cancel-timer ess-rdired-auto-update-timer)))
  169. (defun ess-rdired--tabulated-list-entries (text)
  170. "Return a value suitable for `tabulated-list-entries' from TEXT."
  171. (let (name class length size)
  172. (if (not (string-match-p " +\"" text))
  173. ;; Normal-world
  174. (setq text (split-string text " " t)
  175. name (nth 0 text)
  176. text (cdr text))
  177. ;; Else, someone has spaces in their variable names
  178. (string-match "\"\\([^\"]+\\)" text)
  179. (setq name (substring (match-string 0 text) 1)
  180. text (split-string (substring text (1+ (match-end 0))) " " t)))
  181. (setq class (nth 0 text)
  182. length (nth 1 text)
  183. size (nth 2 text))
  184. (list name
  185. `[(,name
  186. help-echo "mouse-2, RET: View this object"
  187. action ess-rdired-view)
  188. ,class
  189. ,length
  190. ,size])))
  191. (defun ess-rdired-edit ()
  192. "Edit the object at point."
  193. (interactive)
  194. (ess-command (concat "edit(" (tabulated-list-get-id) ")\n")))
  195. (defun ess-rdired-view (&optional _button)
  196. "View the object at point."
  197. (interactive)
  198. (ess-execute (ess-rdired-get (tabulated-list-get-id))
  199. nil "R view" ))
  200. (defun ess-rdired-get (name)
  201. "Generate R code to get the value of the variable NAME.
  202. This is complicated because some variables might have spaces in their names.
  203. Otherwise, we could just pass the variable name directly to *R*."
  204. (concat "get(" (ess-rdired-quote name) ")")
  205. )
  206. (defun ess-rdired-quote (name)
  207. "Quote NAME if not already quoted."
  208. (if (equal (substring name 0 1) "\"")
  209. name
  210. (concat "\"" name "\"")))
  211. (defun ess-rdired-View ()
  212. "View the object at point in its own buffer.
  213. Like `ess-rdired-view', but the object gets its own buffer name."
  214. (interactive)
  215. (let ((objname (tabulated-list-get-id)))
  216. (ess-execute (ess-rdired-get objname)
  217. nil (concat "R view " objname ))))
  218. (defun ess-rdired-plot ()
  219. "Plot the object on current line."
  220. (interactive)
  221. (let ((objname (tabulated-list-get-id)))
  222. (ess-eval-linewise (format "plot(%s)" (ess-rdired-get objname)))))
  223. (defun ess-rdired-type ()
  224. "Run the mode() on command at point."
  225. (interactive)
  226. (let ((objname (tabulated-list-get-id))
  227. ;; create a temp buffer, and then show output in echo area
  228. (tmpbuf (get-buffer-create "**ess-rdired-mode**")))
  229. (if objname
  230. (progn
  231. (ess-command (concat "mode(" (ess-rdired-get objname) ")\n")
  232. tmpbuf )
  233. (set-buffer tmpbuf)
  234. (message "%s" (concat
  235. objname ": "
  236. (buffer-substring (+ 4 (point-min)) (1- (point-max)))))
  237. (kill-buffer tmpbuf)))))
  238. (defalias 'ess-rdired-expunge #'ess-rdired-delete)
  239. (defun ess-rdired-delete ()
  240. "Delete the object at point."
  241. (interactive)
  242. (let ((objname (tabulated-list-get-id)))
  243. (when (yes-or-no-p (format "Really delete %s? " objname))
  244. (ess-eval-linewise (format "rm(%s)" (ess-rdired-quote objname)) nil nil nil t)
  245. (revert-buffer))))
  246. (defun ess-rdired-switch-process ()
  247. "Switch to examine different *R* process.
  248. If you have multiple R processes running, e.g. *R*, *R:2*, *R:3*, you can
  249. use this command to choose which R process you would like to examine.
  250. After switching to a new process, the buffer is updated."
  251. (interactive)
  252. (ess-switch-process)
  253. (ess-rdired))
  254. (defun ess-rdired--length-predicate (A B)
  255. "Enable sorting by length in `ess-rdired' buffers.
  256. Return t if A's length is < than B's length."
  257. (let ((lenA (aref (cadr A) 2))
  258. (lenB (aref (cadr B) 2)))
  259. (< (string-to-number lenA) (string-to-number lenB))))
  260. (defun ess-rdired--size-predicate (A B)
  261. "Enable sorting by size in `ess-rdired' buffers.
  262. Return t if A's size is < than B's size."
  263. (let ((lenA (aref (cadr A) 3))
  264. (lenB (aref (cadr B) 3)))
  265. (< (string-to-number lenA) (string-to-number lenB))))
  266. (define-obsolete-function-alias 'ess-rdired-quit #'quit-window "ESS 19.04")
  267. (define-obsolete-function-alias 'ess-rdired-next-line #'forward-to-indentation "ESS 19.04")
  268. (define-obsolete-function-alias 'ess-rdired-previous-line #'backward-to-indentation "ESS 19.04")
  269. (define-obsolete-function-alias 'ess-rdired-move-to-object #'back-to-indentation "ESS 19.04")
  270. (provide 'ess-rdired)
  271. ;;; ess-rdired.el ends here