<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/rstudioaddins-part-3-view-objects-files-functions-and-more-with-1-keypress/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://jozefhajnala.gitlab.io/r/r103-keypress-viewer/"> Jozef's Rblog</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<div>
<h1>Introduction</h1>
<p>In this post in the RStudio:addins series we will try to make our work more efficient with an addin for better inspection of objects, functions and files within RStudio. RStudio already has a very useful <code>View</code> function and a <code>Go To Function / File</code> feature with F2 as the default keyboard shortcut and yes, I know I promised automatic generation of <code>@importFrom</code> roxygen tags in the <a rel="nofollow" href="https://jozefhajnala.gitlab.io/r/r102-addin-roxytags" target="_blank">previous post</a>, unfortunately we will have to wait a bit longer for that one but I believe this one more than makes up for it in usefulness.</p>
<blockquote>
<h4>The addin we will create in this article will let us use RStudio to View and inspect a wide range of objects, functions and files with 1 keypress.</h4>
</blockquote>
<div>
<img alt="The addins in action" src="https://i0.wp.com/jozefhajnala.gitlab.io/r/img/r103-01-showcase.gif?w=456&ssl=1" data-recalc-dims="1">
<p>The addins in action</p>
</div>
</div>
<div>
<h1>Contents</h1>
<ol>
<li><a rel="nofollow" href="https://jozefhajnala.gitlab.io/r/r103-keypress-viewer/#retrieving-objects-from-sys.frames" target="_blank">Retrieving objects from sys.frames</a></li>
<li><a rel="nofollow" href="https://jozefhajnala.gitlab.io/r/r103-keypress-viewer/#viewing-files-objects-functions-and-more-efficiently" target="_blank">Viewing files, objects and functions and more efficiently</a></li>
<li><a rel="nofollow" href="https://jozefhajnala.gitlab.io/r/r103-keypress-viewer/#the-addin-function-updating-the-.dcf-file-and-key-binding" target="_blank">The addin function, updating the .dcf file and key binding</a></li>
<li><a rel="nofollow" href="https://jozefhajnala.gitlab.io/r/r103-keypress-viewer/#the-addin-in-action" target="_blank">The addin in action</a></li>
<li><a rel="nofollow" href="https://jozefhajnala.gitlab.io/r/r103-keypress-viewer/#tldr---just-give-me-the-package" target="_blank">TL;DR – Just give me the package</a></li>
<li><a rel="nofollow" href="https://jozefhajnala.gitlab.io/r/r103-keypress-viewer/#references" target="_blank">References</a></li>
</ol>
</div>
<div>
<h1>Retrieving objects from sys.frames</h1>
<p>As a first step, we need to be able to retrieve the value of the object we are looking for based on a character string from a frame within the currently present <code>sys.frames()</code> for our session. This may get tricky, as it is not sufficient to only look at parent frames, because we may easily have multiple sets of “parallel” call stacks, especially when executing addins.</p>
<p>An example can be seen in the following screenshot, where we have a <code>browser()</code> call executed during the Addin execution itself. We can see that our current frame is 18 and browsing through its parent would get us to frames <code>17 -> 16 -> 15 -> 14 -> 0</code> (<code>0</code> being the <code>.GlobalEnv</code>). The object we are looking for is however most likely in one of the other frames (<code>9</code> in this particular case):</p>
<div>
<img alt="Example of sys.frames" src="https://i1.wp.com/jozefhajnala.gitlab.io/r/img/r103-02-frames.png?w=456&ssl=1" data-recalc-dims="1">
<p>Example of sys.frames</p>
</div>
<pre><code>getFromSysframes &lt;- function(x) {
  if (!(is.character(x) &amp;&amp; length(x) == 1 &amp;&amp; nchar(x) > 0)) {
    warning(&quot;Expecting a non-empty character of length 1. Returning NULL.&quot;)
    return(invisible(NULL))
  }
  validframes &lt;- c(sys.frames()[-sys.nframe()], .GlobalEnv)
  res &lt;- NULL
  for (i in validframes) {
    inherits &lt;- identical(i, .GlobalEnv)
    res &lt;- get0(x, i, inherits = inherits)
    if (!is.null(res)) {
      return(res)
    }
  }
  return(invisible(res))
}</code></pre>
</div>
<div>
<h1>Viewing files, objects, functions and more efficiently</h1>
<p>As a second step, we write a function to actually view our object in RStudio. We have quite some flexibility here, so as a first shot we can do the following:</p>
<ol>
<li>Open a file if the selection (or the selection with quotes added) is a path to an existing file. This is useful for viewing our scripts, data files, etc. even if they are not quoted, such as the links in your <code>Rmd</code> files</li>
<li>Attempt to retrieve the object by the name and if found, try to use <code>View</code> to view it</li>
<li>If we did not find the object, we can optionally still try to retrieve the value by evaluating the provided character string. This carries some pitfalls, but is very useful for example for
<ul>
<li>viewing elements of lists, vectors, etc. where we need to evaluate <code>[</code>, <code>[[</code> or <code>$</code> to do so.</li>
<li>viewing operation results directly in the viewer, as opposed to writing them out into the console, useful for example for wide matrices that (subjectively) look better in the RStudio viewer, compared to the console output</li>
</ul>
</li>
<li>If the <code>View</code> fails, we can still show useful information by trying to View its structure, enabling us to inspect objects that cannot be coerced to a <code>data.frame</code> and therefore would fail to be viewed.</li>
</ol>
<pre><code>viewObject &lt;- function(chr,
                       tryEval = getOption(&quot;jhaddins_view_tryeval&quot;,
                                           default = TRUE)
                       ) {

  if (!(is.character(chr) &amp;&amp; length(chr) == 1 &amp;&amp; nchar(chr) > 0)) {
    message(&quot;Invalid input, expecting a non-empty character of length 1&quot;)
    return(invisible(1L))
  }

  ViewWrap &lt;- get(&quot;View&quot;, envir = as.environment(&quot;package:utils&quot;))

  # maybe it is an unquoted filename - if so, open it
  if (file.exists(chr)) {
    rstudioapi::navigateToFile(chr)
    return(invisible(0L))
  }
  # or maybe it is a quoted filename - if so, open it
  if (file.exists(gsub(&quot;\&quot;&quot;, &quot;&quot;, chr, fixed = TRUE))) {
    rstudioapi::navigateToFile(gsub(&quot;\&quot;&quot;, &quot;&quot;, chr, fixed = TRUE))
    return(invisible(0L))
  }

  obj &lt;- getFromSysframes(chr)

  if (is.null(obj)) {
    if (isTRUE(tryEval)) {
      # object not found, try evaluating
      try(obj &lt;- eval(parse(text = chr)), silent = TRUE)
    }
    if (is.null(obj)) {
      message(sprintf(&quot;Object %s not found&quot;, chr))
      return(invisible(1L))
    }
  }

  # try to View capturing output for potential errors
  Viewout &lt;- utils::capture.output(ViewWrap(obj, title = chr))
  if (length(Viewout) > 0 &amp;&amp; grepl(&quot;Error&quot;, Viewout)) {
    # could not view, try to at least View the str of the object
    strcmd &lt;- sprintf(&quot;str(%s)&quot;, chr)
    message(paste(Viewout,&quot;| trying to View&quot;, strcmd))
    ViewWrap(utils::capture.output(utils::str(obj)), title = strcmd)
  }

  return(invisible(0L))
}</code></pre>
<p>This function can of course be improved and updated in many ways, for example using the <code>summary</code> method instead of <code>str</code> for selected object classes, or showing contents of <code>.csv</code> (or other data) files already read into a <code>data.frame</code>.</p>
</div>
<div>
<h1>The addin function, updating the .dcf file and key binding</h1>
<p>If you followed the previous posts in the series, you most likely already know what is coming up next. First, we need a function serving as a binding for the addin that will execute out <code>viewObject</code> function on the active document’s selections:</p>
<pre><code>viewSelection &lt;- function() {
  context &lt;- rstudioapi::getActiveDocumentContext()
  lapply(X = context[[&quot;selection&quot;]]
         , FUN = function(thisSel) {
           viewObject(thisSel[[&quot;text&quot;]])
         }
  )
  return(invisible(NULL))
}</code></pre>
<p>Secondly, we update the <code>inst/rstudio/addins.dcf</code> file by adding the binding for the newly created addin:</p>
<pre><code>Name: viewSelection
Description: Tries to use View to View the object defined by a text selected in RStudio
Binding: viewSelection
Interactive: false</code></pre>
<p>Finally, we re-install the package and assign the keyboard shortcut in the <code>Tools -> Addins -> Browse Addins... -> Keyboard Shortcuts...</code> menu. Personally I assigned a single <code>F4</code> keystroke for this, as I use it very often:</p>
<div>
<img alt="Assigning a keyboard shortcut to use the Addin" src="https://i1.wp.com/jozefhajnala.gitlab.io/r/img/r103-03-key-binding.gif?w=456&ssl=1" data-recalc-dims="1">
<p>Assigning a keyboard shortcut to use the Addin</p>
</div>
</div>
<div>
<h1>The addin in action</h1>
<p>Now, let’s view a few files, a <code>data.frame</code>, a <code>function</code> and a <code>try-error</code> class object just pressing <code>F4</code>. <img alt="The addins in action" src="https://i0.wp.com/jozefhajnala.gitlab.io/r/img/r103-01-showcase.gif?w=456&ssl=1" data-recalc-dims="1"></p>
</div>
<div>
<h1>TL;DR – Just give me the package</h1>
<ul>
<li>get the <a rel="nofollow" href="https://gitlab.com/jozefhajnala/jhaddins/tags/addins3-viewer" target="_blank">status of the package after this article</a></li>
<li>or use <code>git clone</code> from <a rel="nofollow" href="https://gitlab.com/jozefhajnala/jhaddins.git" target="_blank"><code>https://gitlab.com/jozefhajnala/jhaddins.git</code></a></li>
</ul>
</div>
<div>
<h1>References</h1>
<ul>
<li><a rel="nofollow" href="http://adv-r.had.co.nz/Environments.html" target="_blank">Environments chapter</a> of Advanced R</li>
<li><a rel="nofollow" href="https://support.rstudio.com/hc/en-us/articles/205175388-Using-the-Data-Viewer" target="_blank">Using RStudio’s Data Viewer</a></li>
</ul>
<div>
<h2>Did you find the article helpful or interesting? Help others find it by sharing</h2>
<p></p>
<div></div>
</div>
</div>
	<p>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://jozefhajnala.gitlab.io/r/r103-keypress-viewer/"> Jozef's Rblog</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a rel="nofollow" href="https://www.r-project.org/" title="The R Project for Statistical Computing">R</a> news and <a rel="nofollow" href="https://www.r-bloggers.com/search/tutorial" title="R tutorials">tutorials</a> on topics such as: <a rel="nofollow" href="https://www.r-bloggers.com/search/data%20science" title="Data science">Data science</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Big%20Data" title="Big Data">Big Data, <a rel="nofollow" href="https://www.r-users.com/" title="R jobs">R jobs</a>, visualization (<a rel="nofollow" href="https://www.r-bloggers.com/search/ggplot2" title="ggplot and ggplot2 tutorials">ggplot2</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/boxplot" title="Boxplots using lattice and ggplot2 tutorials">Boxplots</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/map" title="Maps and gis">maps</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/animation" title="Animation in R">animation</a>), programming (<a rel="nofollow" href="https://www.r-bloggers.com/search/RStudio" title="RStudio IDE for R">RStudio</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/sweave" title="Sweave and literate programming">Sweave</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/LaTeX" title="LaTeX in R">LaTeX</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/SQL" title="SQL and databases">SQL</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/eclipse" title="Eclipse IDE for R">Eclipse</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/git" title="git and github, Version Control System">git</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/hadoop" title="Large data in R using Hadoop">hadoop</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Web+Scraping" title="Web Scraping of google, facebook, yahoo, twitter and more using R">Web Scraping</a>) statistics (<a rel="nofollow" href="https://www.r-bloggers.com/search/regression" title="Regressions and ANOVA analysis tutorials">regression</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/PCA" title="principal component analysis tutorial">PCA</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/time+series" title="Time series">time series</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/trading" title="finance trading">trading</a>) and more...
<img width="1" alt="" src="http://feeds.feedburner.com/~r/RBloggers/~4/xipxmZD6WZE" height="1"></a></div>