Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
2701 views
1
;;; ess-rdired.el --- prototype object browser for R, looks like dired mode.
2
3
;; Copyright (C) 2002--2004 A.J. Rossini, Richard M. Heiberger, Martin
4
;; Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen.
5
6
;; Author: Stephen Eglen <[email protected]>
7
;; Created: Thu 24 Oct 2002
8
;; Maintainer: ESS-core <[email protected]>
9
10
;; This file is part of ESS
11
12
;; This file is not part of GNU Emacs.
13
14
;; ess-rdired.el is free software; you can redistribute it and/or modify
15
;; it under the terms of the GNU General Public License as published by
16
;; the Free Software Foundation; either version 2, or (at your option)
17
;; any later version.
18
19
;; ess-rdired.el is distributed in the hope that it will be useful,
20
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
;; GNU General Public License for more details.
23
24
;; A copy of the GNU General Public License is available at
25
;; http://www.r-project.org/Licenses/
26
27
;; This provides a dired-like buffer for R objects. Instead of
28
;; operating on files, we operate on R objects in the current
29
;; environment. Objects can be viewed, edited, deleted, plotted and
30
;; so on.
31
32
;;; Commentary:
33
34
;; Installation and usage.
35
;;
36
;; Load in this library, e.g. with the command:
37
;; (autoload 'ess-rdired "ess-rdired" "View *R* objects in a dired-like buffer." t)
38
;;
39
;; After loading this file, do "M-x R" to start an R session, then
40
;; create a few variables:
41
;; s <- sin(seq(from=0, to=8*pi, length=100))
42
;; x <- c(1, 4, 9)
43
;; y <- rnorm(20)
44
;; z <- TRUE
45
46
;; Then in Emacs, do "M-x ess-rdired" and you should see the following in
47
;; the buffer *R dired*:
48
;; mode length
49
;; s numeric 100
50
;; x numeric 3
51
;; y numeric 20
52
;; z logical 1
53
54
;; Type "?" in the buffer to see the documentation. e.g. when the
55
;; cursor is on the line for `s', type 'p' to plot it, or `v' to view
56
;; its contents in a buffer. Then type 'd' to mark it for deletion.
57
58
;; How it works.
59
60
;; Most of the hardwork is done by the R routine .rdired.objects(),
61
;; which, when called, produces the list of objects in a tidy format.
62
;; This function is stored within the lisp variable `ess-rdired-objects',
63
;; and can be altered to provide other information if you so need it.
64
;; (Martin Maechler suggested providing output from str() here.)
65
66
;; Tested on Emacs 21.2, 21.3 pretest and XEmacs 21.1.14, using R 1.6.
67
68
;; Todo - compare functionality with ess-mouse-me (ess-mous.el).
69
70
;; Todo - How to select alternative environments? Currently only
71
;; shows objects in the .GlobalEnv? See BrowseEnv() in 1.6.x for way
72
;; of browsing other environments.
73
74
;; Todo - problem with fix -- have to wait for fix() command to return
75
;; before *R* buffer can be used again. This can get stuck, umm. not
76
;; sure what is going wrong here. Maybe add a hook to the temp buffer
77
;; so that when buffer is killed, we send an instruction to R to
78
;; update the value of the variable to the contents of the buffer.
79
;; This way *R* doesn't have to wait.
80
81
;; Todo - small bug in .rdired.objects -- if we have a variable called
82
;; `my.x', its value is replaced by the value of my.x used in the
83
;; sapply() calls within .rdired.objects().
84
85
;;; Code:
86
87
(defvar ess-rdired-objects "{.rdired.objects <- function(objs) {
88
if (length(objs)==0) {
89
\"No objects to view!\"
90
} else {
91
mode <- sapply(objs, function(my.x) {
92
eval( parse( text=sprintf('data.class(get(\"%s\"))', my.x))) })
93
length <- sapply(objs, function(my.x) {
94
eval( parse( text=sprintf('length(get(\"%s\"))', my.x))) })
95
d <- data.frame(mode, length)
96
97
var.names <- row.names(d)
98
99
## If any names contain spaces, we need to quote around them.
100
quotes = rep('', length(var.names))
101
spaces = grep(' ', var.names)
102
if (any(spaces))
103
quotes[spaces] <- '\"'
104
var.names = paste(quotes, var.names, quotes, sep='')
105
row.names(d) <- paste(' ', var.names, sep='')
106
d
107
}
108
}; cat('\n'); print(.rdired.objects(ls()))}\n"
109
"Function to call within R to print information on objects. The last
110
line of this string should be the instruction to call the
111
function which prints the output for rdired.")
112
113
(defvar ess-rdired-buffer "*R dired*"
114
"Name of buffer for displaying R objects.")
115
116
(defvar ess-rdired-mode-map
117
(let ((ess-rdired-mode-map (make-sparse-keymap)))
118
119
(define-key ess-rdired-mode-map "?" 'ess-rdired-help)
120
(define-key ess-rdired-mode-map "d" 'ess-rdired-delete)
121
(define-key ess-rdired-mode-map "u" 'ess-rdired-undelete)
122
(define-key ess-rdired-mode-map "x" 'ess-rdired-expunge)
123
;; editing requires a little more work.
124
;;(define-key ess-rdired-mode-map "e" 'ess-rdired-edit)
125
(define-key ess-rdired-mode-map "v" 'ess-rdired-view)
126
(define-key ess-rdired-mode-map "V" 'ess-rdired-View)
127
(define-key ess-rdired-mode-map "p" 'ess-rdired-plot)
128
(define-key ess-rdired-mode-map "s" 'ess-rdired-sort)
129
(define-key ess-rdired-mode-map "q" 'ess-rdired-quit)
130
(define-key ess-rdired-mode-map "y" 'ess-rdired-type) ;what type?
131
(define-key ess-rdired-mode-map " " 'ess-rdired-next-line)
132
(define-key ess-rdired-mode-map [backspace] 'ess-rdired-previous-line)
133
(define-key ess-rdired-mode-map "\C-n" 'ess-rdired-next-line)
134
(define-key ess-rdired-mode-map "\C-p" 'ess-rdired-previous-line)
135
;; (define-key ess-rdired-mode-map "n" 'ess-rdired-next-line)
136
;; (define-key ess-rdired-mode-map "p" 'ess-rdired-previous-line)
137
138
;; R mode keybindings.
139
(define-key ess-rdired-mode-map "\C-c\C-s" 'ess-rdired-switch-process)
140
(define-key ess-rdired-mode-map "\C-c\C-y" 'ess-switch-to-ESS)
141
(define-key ess-rdired-mode-map "\C-c\C-z" 'ess-switch-to-end-of-ESS)
142
143
(define-key ess-rdired-mode-map [down] 'ess-rdired-next-line)
144
(define-key ess-rdired-mode-map [up] 'ess-rdired-previous-line)
145
(define-key ess-rdired-mode-map "g" 'revert-buffer)
146
(if (featurep 'xemacs)
147
(define-key ess-rdired-mode-map [button2] 'ess-rdired-mouse-view)
148
(define-key ess-rdired-mode-map [mouse-2] 'ess-rdired-mouse-view)
149
)
150
ess-rdired-mode-map))
151
152
(defun ess-rdired-mode ()
153
"Major mode for output from `ess-rdired'.
154
`ess-rdired' provides a dired-like mode for R objects. It shows the
155
list of current objects in the current environment, one-per-line. You
156
can then examine these objects, plot them, and so on.
157
\\{ess-rdired-mode-map}"
158
;; (kill-all-local-variables)
159
(make-local-variable 'revert-buffer-function)
160
(setq revert-buffer-function 'ess-rdired-revert-buffer)
161
(use-local-map ess-rdired-mode-map)
162
(setq major-mode 'ess-rdired-mode)
163
(setq mode-name (concat "RDired " ess-local-process-name))
164
(run-mode-hooks 'ess-rdired-mode-hook))
165
166
(defun ess-rdired-mode-hook nil
167
"Run upon entering `ess-rdired-mode'.")
168
169
(defvar ess-rdired-sort-num nil) ;silence the compiler.
170
;; but see following defun -- maybe it should be buffer local.
171
172
(defun ess-rdired ()
173
"Run dired-like mode on R objects.
174
This is the main function. See documentation for `ess-rdired-mode' though
175
for more information!"
176
(interactive)
177
(let ((proc ess-local-process-name)
178
(buff (get-buffer-create ess-rdired-buffer)))
179
180
(ess-command ess-rdired-objects buff)
181
(ess-setq-vars-local (symbol-value ess-local-customize-alist) buff)
182
183
(with-current-buffer buff
184
(setq ess-local-process-name proc)
185
(ess-rdired-mode)
186
187
;; When definiting the function .rdired.objects(), a "+ " is printed
188
;; for every line of the function definition; these are deleted
189
;; here.
190
(goto-char (point-min))
191
(delete-region (point-min) (1+ (point-at-eol)))
192
193
;; todo: not sure how to make ess-rdired-sort-num buffer local?
194
;;(set (make-local-variable 'ess-rdired-sort-num) 2)
195
;;(make-variable-buffer-local 'ess-rdired-sort-num)
196
(setq ess-rdired-sort-num 1)
197
(ess-rdired-insert-set-properties (save-excursion
198
(goto-char (point-min))
199
(forward-line 1)
200
(point))
201
(point-max))
202
(setq buffer-read-only t)
203
)
204
205
(pop-to-buffer buff)
206
))
207
208
209
(defun ess-rdired-object ()
210
"Return name of object on current line.
211
Handle special case when object contains spaces."
212
(save-excursion
213
(beginning-of-line)
214
(forward-char 2)
215
216
(cond ((looking-at " ") ; First line?
217
nil)
218
((looking-at "\"") ; Object name contains spaces?
219
(let (beg)
220
(setq beg (point))
221
(forward-char 1)
222
(search-forward "\"")
223
(buffer-substring-no-properties beg (point))))
224
(t ;should be a regular object.
225
(let (beg)
226
(setq beg (point))
227
(search-forward " ") ;assume space follows object name.
228
(buffer-substring-no-properties beg (1- (point))))))))
229
230
(defun ess-rdired-edit ()
231
"Edit (fix) the object at point."
232
(interactive)
233
(let ((objname (ess-rdired-object)))
234
(ess-command (concat "edit(" objname ")\n"))))
235
236
(defun ess-rdired-view ()
237
"View the object at point."
238
(interactive)
239
(let ((objname (ess-rdired-object)))
240
(ess-execute (ess-rdired-get objname)
241
nil "R view" )))
242
243
(defun ess-rdired-get (name)
244
"Generate R code to get the value of the variable name.
245
This is complicated because some variables might have spaces in their names.
246
Otherwise, we could just pass the variable name directly to *R*."
247
(concat "get(" (ess-rdired-quote name) ")")
248
)
249
250
(defun ess-rdired-quote (name)
251
"Quote name if not already quoted."
252
(if (equal (substring name 0 1) "\"")
253
name
254
(concat "\"" name "\"")))
255
256
257
(defun ess-rdired-View ()
258
"View the object at point in its own buffer.
259
Like `ess-rdired-view', but the object gets its own buffer name."
260
(interactive)
261
(let ((objname (ess-rdired-object)))
262
(ess-execute (ess-rdired-get objname)
263
nil (concat "R view " objname ))))
264
265
(defun ess-rdired-plot ()
266
"Plot the object on current line."
267
(interactive)
268
(let ((objname (ess-rdired-object)))
269
(ess-eval-linewise (format "plot(%s)" (ess-rdired-get objname)))))
270
271
(defun ess-rdired-type ()
272
"Run the mode() on command at point.
273
Named type because of similarity with the dired command bound to
274
y key."
275
(interactive)
276
(let ((objname (ess-rdired-object))
277
;; create a temp buffer, and then show output in echo area
278
(tmpbuf (get-buffer-create "**ess-rdired-mode**")))
279
(if objname
280
(progn
281
(ess-command (concat "mode(" (ess-rdired-get objname) ")\n")
282
tmpbuf )
283
(set-buffer tmpbuf)
284
(message (concat
285
objname ": "
286
(buffer-substring (+ 4 (point-min)) (1- (point-max)))))
287
(kill-buffer tmpbuf)))))
288
289
(defun ess-rdired-delete (arg)
290
"Mark the current (or next ARG) objects for deletion.
291
If point is on first line, all objects are marked for deletion."
292
(interactive "p")
293
(ess-rdired-mark "D" arg))
294
295
(defun ess-rdired-undelete (arg)
296
"Unmark the current (or next ARG) objects.
297
If point is on first line, all objects will be unmarked."
298
(interactive "p")
299
(ess-rdired-mark " " arg))
300
301
(defun ess-rdired-mark (mark-char arg)
302
"Mark the object, using MARK-CHAR, on current line (or next ARG lines)."
303
;; If we are on first line, mark all lines.
304
(let ((buffer-read-only nil)
305
move)
306
(if (eq (point-min)
307
(save-excursion (beginning-of-line) (point)))
308
(progn
309
;; we are on first line, so make a note of point, and count
310
;; how many objects we want to delete. Then at end of defun,
311
;; restore point.
312
(setq move (point))
313
(forward-line 1)
314
(setq arg (count-lines (point) (point-max)))))
315
(while (and (> arg 0) (not (eobp)))
316
(setq arg (1- arg))
317
(beginning-of-line)
318
(progn
319
(insert mark-char)
320
(delete-char 1)
321
(forward-line 1)))
322
(if move
323
(goto-char move))))
324
325
326
(defun ess-rdired-expunge ()
327
"Delete the marked objects.
328
User is queried first to check that objects should really be deleted."
329
(interactive)
330
(let ((objs "rm(")
331
(count 0))
332
(save-excursion
333
(goto-char (point-min)) (forward-line 1)
334
(while (< (count-lines (point-min) (point))
335
(count-lines (point-min) (point-max)))
336
(beginning-of-line)
337
(if (looking-at "^D ")
338
(setq count (1+ count)
339
objs (concat objs (ess-rdired-object) ", " )))
340
(forward-line 1)
341
))
342
(if (> count 0)
343
;; found objects to delete
344
(progn
345
(setq objs (concat
346
(substring objs 0 (- (length objs) 2))
347
")\n"))
348
(if (yes-or-no-p (format "Delete %d %s " count
349
(if (> count 1) "objects" "object")))
350
(progn
351
(ess-eval-linewise objs nil nil nil 'wait)
352
(ess-rdired)
353
)))
354
;; else nothing to delete
355
(message "no objects set to delete")
356
)))
357
358
;; Fancy delete method, based on dired. Bit too much for our needs?
359
;; (defun ess-rdired-expunge ()
360
;; "Delete the marked objects.
361
;; User is queried first to check that objects should really be deleted."
362
;; (interactive)
363
;; (let ((objs)
364
;; (cmd "rm("))
365
;; (save-excursion
366
;; (goto-line 2)
367
;; (while (< (count-lines (point-min) (point))
368
;; (count-lines (point-min) (point-max)))
369
;; (beginning-of-line)
370
;; (if (looking-at "^D ")
371
;; (progn
372
;; (setq objs (cons (ess-rdired-object) objs ))
373
;; (setq cmd (concat cmd (ess-rdired-object) ", "))
374
;; ))
375
;; (forward-line 1)
376
;; ))
377
;; (if (> (length objs) 0)
378
;; ;; found objects to delete
379
;; (if
380
;; (dired-mark-pop-up "*RDired deletions*" 'delete
381
;; objs dired-deletion-confirmer
382
;; (format "delete %s "
383
;; (dired-mark-prompt nil objs)))
384
;; ;; should delete the objects.
385
;; (progn
386
;; (setq cmd (concat (substring cmd 0 (- (length cmd) 2))
387
;; ")\n"))
388
;; (ess-command cmd)
389
;; (ess-rdired)))
390
;; ;; else nothing to delete
391
;; (message "no objects set to delete")
392
;; )))
393
394
(defun ess-rdired-quit ()
395
"Quit the R dired buffer."
396
(interactive)
397
(kill-buffer ess-rdired-buffer))
398
399
(defun ess-rdired-revert-buffer (ignore noconfirm)
400
"Update the buffer list (in case object list has changed).
401
Arguments IGNORE and NOCONFIRM currently not used."
402
(ess-rdired))
403
404
(defun ess-rdired-help ()
405
"Show help for `ess-rdired-mode'."
406
(interactive)
407
(describe-function 'ess-rdired-mode))
408
409
(defun ess-rdired-sort ()
410
"Sort the rdired output according to one of the columns.
411
Rotate between the alternative sorting methods."
412
(interactive)
413
(setq ess-rdired-sort-num (1+ ess-rdired-sort-num))
414
(let ((buffer-read-only nil)
415
(beg (save-excursion
416
(goto-char (point-min))
417
(forward-line 1)
418
(point)))
419
(end (point-max)))
420
(if (> ess-rdired-sort-num 3)
421
(setq ess-rdired-sort-num 1))
422
(cond ((eq ess-rdired-sort-num 1)
423
(sort-fields 1 beg end))
424
((eq ess-rdired-sort-num 2)
425
(sort-fields 2 beg end))
426
((eq ess-rdired-sort-num 3)
427
(sort-numeric-fields 3 beg end)))))
428
429
(defun ess-rdired-next-line (arg)
430
"Move down lines then position at object.
431
Optional prefix ARG says how many lines to move; default is one line."
432
(interactive "p")
433
(forward-line arg)
434
(ess-rdired-move-to-object))
435
436
(defun ess-rdired-previous-line (arg)
437
"Move up lines then position at object.
438
Optional prefix ARG says how many lines to move; default is one line."
439
(interactive "p")
440
(forward-line (- (or arg 1))) ; -1 if arg was nil
441
(ess-rdired-move-to-object))
442
443
(defun ess-rdired-move-to-object ()
444
"Put point at start of object."
445
(beginning-of-line)
446
(forward-char 2)
447
)
448
449
(defun ess-rdired-mouse-view (event)
450
"In rdired, visit the object on the line you click on."
451
(interactive "e")
452
(let (window pos)
453
(save-excursion
454
(if (featurep 'xemacs)
455
;; XEmacs
456
(setq window (event-window event)
457
pos (event-point event))
458
;; Emacs
459
(setq window (posn-window (event-end event))
460
pos (posn-point (event-end event))))
461
(if (not (windowp window))
462
(error "No file chosen"))
463
(set-buffer (window-buffer window))
464
(goto-char pos)
465
(ess-rdired-view))))
466
467
(defun ess-rdired-insert-set-properties (beg end)
468
"Add mouse highlighting to each object name in the R dired buffer."
469
(save-excursion
470
(goto-char beg)
471
(while (< (point) end)
472
(ess-rdired-move-to-object)
473
(add-text-properties
474
(point)
475
(save-excursion
476
(search-forward " ")
477
(1- (point)))
478
'(mouse-face highlight
479
help-echo "mouse-2: view object in other window"))
480
(forward-line 1))))
481
482
(defun ess-rdired-switch-process ()
483
"Switch to examine different *R* process.
484
If you have multiple R processes running, e.g. *R*, *R:2*, *R:3*, you can
485
use this command to choose which R process you would like to examine.
486
After switching to a new process, the buffer is updated."
487
(interactive)
488
(ess-switch-process)
489
(ess-rdired))
490
491
;;; ess-rdired.el ends here.
492
493