Default CRAN Mirror
local({r <- getOption("repos")
"CRAN"] <- "https://mirror.csclub.uwaterloo.ca/CRAN/"
r[options(repos=r)})
June 25, 2023
The Nvim-R plugin extends a lot of RStudio functionality to Neovim but the default plotting experience leaves a lot to be desired.
While RStudio levels of integration may be out of reach there are several ways it can be improved. Doing so just requires going outside of Neovim.
Here is what it will look like when its all done.
This guide assumes you already have Neovim and Nvim-R installed.
GUI editors like RStudio and VSCode can quite easily integrate plots into a pane either by defining their own output device or redirecting the output of an existing device to the application.
Terminal based editor’s like Neovim do not support graphics and so cannot display the output inside the app. Instead they delegate the graphics to a separate program running in a separate window. By default this leaves Nvim-R plots popping up in the middle of your screen, and falling behind your Neovim window with every edit you make. Additionally the default plotting window does not keep a plot history so each new plot overwrites the previous one.
Luckily improving the experience is as simple as configuring the behaviour of that external window.
The default window device used by R depends on your operating system but they all have a fairly consistent set of options that can be specified either as defaults through the .options method or on window creation as arguments.
I’ll be using the windows
device. Linux users should use x11
and MacOS users quartz
.
There may be minor differences between the options and behaviour of these devices. Check the R documentation for these devices under the grDevices
library if things aren’t behaving as expected.
The best place to change the default behaviour of the plotting window is in a .Rprofile file. This script runs when R starts and can be used to configure all sorts of user preferences. R looks for .Rprofile in three places: the current directory, the home directory, and the installation directory.
The user home directory can be found with the command path.expand("~")
. It is not the same as the user’s home directory. Mine is in my Documents folder.
Create a text file in that directory called .Rprofile
and write print("Loaded .Rprofile")
. If everything is in the right place, when you open an R terminal you should see the message below the startup text.
Unfortunately .Rprofile is loaded before the grDevices library so the windows.options
method can’t be called directly. Instead you have to set a hook that will wait until grDevices is ready.
The settings I use are:
grDevices::windows.options(
# Save the plot history
record = TRUE,
# Position the display in the bottom right corner of main monitor
width = 4,
height = 3,
xpos = 1273,
ypos = 668
)
if (interactive()) {
# Open an empty window with a distinctive title for ahk window detection
grDevices::windows(title = "Nvim-R Plot")
# Set that window to stay on top
grDevices::bringToTop(which = grDevices::dev.cur(), stay = TRUE)
}
This overcomes all the main issues with the default plotting experience. The plot history is retained, the window is contained to a defined zone of the screen, and it always stays on top of Neovim.
For finishing touches we can configure Nvim-R to create an extra buffer for the plotting window to go over. I do this by setting these options in my Nvim-R config.
-- Create empty pane at bottom right
vim.g.R_after_start = {':winc j', ':vsplit', ':vertical resize ' .. math.floor(vim.fn.winwidth(0) / 3), ":winc k"}
-- Place object browser above plot pane
vim.g.R_objbr_place = 'script,right'
This ensures console and object browser output is not blocked by the plot.
Then I use Autohotkey to hide the plot window’s title bar whenever I start R from Neovim with the \rf
keymapping.
#HotIf WinActive("ahk_exe WindowsTerminal.exe")
:*?B0:\rf:: {
win := WinWait("Nvim-R Plot", , 10)
if win {
WinActivate win ; to stop the notification
WinSetStyle "-0xC00000", win
Sleep 50
WinActivate "ahk_exe WindowsTerminal.exe"
}
}
Finally, while opening in the bottom corner is a big improvement from the default you can make things even more precise with some more AHK.
This function finds the location of the pane from a screenshot of the pane intersection and automatically moves the plot window into position.
MovePlot() {
plot := WinExist("Nvim-R Plot")
if not plot
return
WinGetPos , , &width, &height, "A"
WinSetAlwaysontop True, "A"
if ImageSearch(&x, &y, 0, 0, A_ScreenWidth, A_ScreenHeight, "r_plot_location.png")
WinMove x + 1, y + 3, width - x - 10, height - y - 70, plot
WinSetAlwaysontop False, "A"
}
ScrollLock::MovePlot()
#HotIf
I call this function on plot open as well as with the ScrollLock hotkey when I move the window.