Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .config/nvim/lua/config/autocmds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,16 @@ vim.api.nvim_create_autocmd("ModeChanged", {
vim.wo.cursorline = vim.api.nvim_get_mode().mode == "nt"
end,
})


-- Always use insert mode when entering a terminal buffer, even with mouse click.
-- NOTE: Clicking with a mouse a second time enters visual select mode, just like in a text buffer.
vim.api.nvim_create_autocmd({ "BufEnter", "WinEnter" }, {
pattern = "*",
callback = function()
if vim.bo.buftype == "terminal" then
vim.cmd("startinsert")
end
end,
})

65 changes: 65 additions & 0 deletions .config/nvim/lua/plugins/r-nvim.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
-- Integration with a running R interpreter
return {
"R-nvim/R.nvim",

-- Current latest version (0.99.5) doesn't fully support old R versions.
version="0.99.4",

-- Only load the plugin if we can find an executable for R.
cond = function()
return vim.fn.executable("R") == 1
end,

opts = {
-- These support syntax highlighting in the R interpreter
Rout_more_colors=true,
Rout_follow_colorscheme=true,
-- config_tmux=false,
rconsole_width=80,

-- by default, :RDSendLine will send the entire block (entire for-loop or entire function, for example). Disabling this sends just one line at a time.
parenblock=false,
min_editor_width=18,
view_df = {
-- open a dataframe with <localleader>rv in visidata in a new nvim terminal window.
-- Note: using tmux as per the docs does not work when on an interactive
-- node, so we use local terminal here.
open_app = "terminal:vd",
},
r_ls = {
-- Completion lists are created by the built-in language server per
-- package, per version. It can take a while to build completion lists,
-- especially when you bounce between environments. They are cached
-- though, so it's a one-time cost. If you're OK paying that cost for
-- convenient completion, set completion = true (which is the default)
completion = false,
},

hook = {
on_filetype = function()
vim.api.nvim_buf_set_keymap(0, "n", "gxx", "<Plug>RDSendLine", {})
vim.api.nvim_buf_set_keymap(0, "v", "gx", "<Plug>RSendSelection", {})
vim.api.nvim_buf_set_keymap(0, "n", ",cd", "<Plug>RDSendChunk", {})
vim.api.nvim_buf_set_keymap(0, "n", "<leader>k", "<Plug>RSend rmarkdown::render('%p')<CR>", {})
vim.keymap.set("n", "<leader>k",
function()
local path = vim.api.nvim_buf_get_name(0)
require("r.send").cmd('rmarkdown::render("' .. path .. '")')
end,
{ buffer = true, desc = "Render Rmarkdown" })

-- In 0.99.4 which is used here for compatibility with older
-- R versions, the RDSendLine command sends the entire treesitter block
-- (entire function, entire for-loop, etc) rather than just one line.
-- This is fixed in 0.99.5, but patching here so we can use it with 0.99.4.
-- The behavior is still accessible with the default <localleader>d.
vim.keymap.set("n", "gxx", function()
local line = vim.api.nvim_get_current_line()
require("r.send").cmd(line)
vim.cmd("normal! j")
end, { buffer = true })
end,
},
},
lazy=false
}
9 changes: 0 additions & 9 deletions .config/nvim/lua/plugins/toggleterm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@ return {
end,
})

-- Always use insert mode when entering a terminal buffer, even with mouse click.
-- NOTE: Clicking with a mouse a second time enters visual select mode, just like in a text buffer.
vim.api.nvim_create_autocmd("BufEnter", {
pattern = "*",
callback = function()
vim.cmd("if &buftype == 'terminal' | startinsert | endif")
end,
})

-- Patch toggleterm to use bracketed paste (special escape codes before
-- and after the text to be pasted)
-- https://en.wikipedia.org/wiki/Bracketed-paste
Expand Down
87 changes: 87 additions & 0 deletions .functions
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,90 @@ function git-clean-branches () {

echo "Use 'git-clean-branches | xargs git branch -d' to actually delete these." >&2
}



# nvimcom is the R package that the Neovim plugin R.nvim uses to talk to
# a running R session. It's a compiled package, so it must be built against the
# exact R version in use.
#
# To support multiple R versions side by side, we install it into a per-version
# "sidecar" library under ~/R/nvimcom/<version> rather than into R's default
# user library. The correct nvimcom package is added to R_LIBS_USER with the
# Ropen function.
#
# In this way, we can use R.nvim with any version of R without installing it
# into the respective R's environment.

# Echo the X.Y.Z version of the R currently on PATH (empty + nonzero on failure).
_r_version() {
command -v R &>/dev/null || return 1
R --version 2>/dev/null | grep -Eom1 '[0-9]+\.[0-9]+\.[0-9]+'
}

install-nvimcom() {
local nvimcom_src bundled_version r_version sidecar_lib installed_desc install_log min_r

# nvimcom's source ships inside the R.nvim plugin (installed via lazy.nvim).
nvimcom_src="$HOME/.local/share/nvim/lazy/R.nvim/nvimcom"
[[ -d "$nvimcom_src" ]] || {
echo "ERROR: nvimcom source not found at $nvimcom_src."
return 1
}

r_version=$(_r_version) || {
echo "ERROR: No R on PATH."
return 1
}
bundled_version=$(awk '/^Version:/{print $2}' "$nvimcom_src/DESCRIPTION")
sidecar_lib="$HOME/R/nvimcom/$r_version"
installed_desc="$sidecar_lib/nvimcom/DESCRIPTION"

# Refuse to build against an R that's too old. nvimcom is compiled C code;
# a too-old R compiles fine (C only warns on implicit declarations) but
# fails at load with a cryptic "undefined symbol" error. Catch it here
# instead by honoring the minimum R version from nvimcom's DESCRIPTION.
min_r=$(grep -oE 'R \(>= [0-9]+\.[0-9]+(\.[0-9]+)?\)' "$nvimcom_src/DESCRIPTION" \
| grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?')
if [[ -n "$min_r" ]] &&
[[ "$(printf '%s\n%s\n' "$min_r" "$r_version" | sort -V | head -1)" != "$min_r" ]]; then
echo "ERROR: nvimcom $bundled_version requires R >= $min_r, but R $r_version is on PATH."
echo " Put a newer R on PATH, or pin an older R.nvim/nvimcom."
return 1
fi

# Skip the build if the sidecar already has the matching version installed.
if [[ -f "$installed_desc" ]] &&
[[ "$(awk '/^Version:/{print $2}' "$installed_desc")" == "$bundled_version" ]]; then
return 0
fi

# Build into the sidecar. R_LIBS_USER is set so build-time dependency
# lookups also resolve against the sidecar.
echo "Installing nvimcom $bundled_version for R $r_version ..."
mkdir -p "$sidecar_lib"
install_log="/tmp/nvimcom-install-$r_version.log"
if R_LIBS_USER="$sidecar_lib" R CMD INSTALL --library="$sidecar_lib" "$nvimcom_src" >"$install_log" 2>&1; then
echo "✓ nvimcom $bundled_version installed for R $r_version."
else
echo "✗ Failed — see $install_log"
return 1
fi
}

Ropen() {
# Opens $EDITOR (Neovim) for an R session, making sure the version-matched
# nvimcom sidecar library exists and is on R_LIBS_USER so R.nvim can use it.
local r_version sidecar_lib
r_version=$(_r_version) || {
echo "No R found on PATH."
return 1
}

install-nvimcom || return 1

# Prepend the sidecar to R_LIBS_USER (keeping the normal user library too).
sidecar_lib="$HOME/R/nvimcom/$r_version"
R_LIBS_USER="$sidecar_lib:${R_LIBS_USER:-$HOME/R/library}" $EDITOR "$@"
}

12 changes: 12 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
Changelog
=========

2026-06-14
----------

**vim**

Add experimental R.nvim plugin support, including additional functions
``Ropen`` and ``install-nvimcom`` in :file:`.functions`.

Currently not fully functional due to tab completion database resource usage.


2026-05-28
----------

**vim**

- Add [pasteimg](https://github.com/daler/pasteimg.nvim) plugin
- Remove ``img-clip`` plugin (which didn't work on remote systems)
- Improve toggleterm "non-insert mode" cursorline:
- use the Comment color of the initially-loaded colorscheme instead of a hard-coded red
- don't clobber the custom background that toggleterm sets
Expand Down
128 changes: 126 additions & 2 deletions docs/nvim-plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1508,10 +1508,134 @@ See the docs for the plugin on how to set it up, including a macOS Shortcut.
.. plugin-metadata::
:name: pasteimg

.. _imgclip_ref:
.. _rnvim_ref:

``r.nvim``
~~~~~~~~~~

`R.nvim <https://github.com/R-nvim/R.nvim/>`__ provides tight integration with R running in an nvim terminal.

.. warning::

**This is experimental.**

Currently the biggest issue is the creation of the tab-completion database.
``nvimcom`` does this at startup, and with no apparent working way of
disabling it on the nvim side. It does this for *each installed R library*.
If you use this on an all-in-one installation, like the one for Biowulf's
``module load R``, it will consume all resources on the node.

For something like a project-specific R environment, it does take a couple
minutes the first time on a big node, but using the same env after that is
instant.

Another issue is support for old R versions. Currently, R.nvim v0.99.4
cleanly supports old R versions but 0.99.5 does not (despite release notes
to the contrary). This will likely change since the plugin is under active
development.


This provides more features than simply running R in an nvim terminal (like
with :ref:`toggleterm_ref`).

- R object browser (like the panel in RStudio)
- quickly view a dataframe in Visidata (like``View()`` in RStudio)
- tab-completion of functions and arguments
- syntax highlighting in the R interpreter
- R documentation within text buffer
- An R language server that doesn't itself require R (specifically, it doesn't
require being installed into the same environment as R)
- More flexibility in controlling what is sent to the terminal
- Insert commented version of output into the text buffer (great for
``head(df)`` so your comments reflect what's in the data.frame)
- Send ``str`` with the name of the object under the cursor for quick inspection in R
- Jump through an RMarkdown file by chunk
- Lots more...

In order for this to work, the ``nvimcom`` package needs to be installed into
the environment with R. However, it didn't seem right to "contaminate" an
otherwise reproducible R environment with a package just for personal editing
preferences.

To use this additional functionality, you need to do the following two steps:

- Activate the environment you want to use, on the host you want to use. If
you're running R on an interactive node, you need to be *running nvim on the
interactive node*.
- Use ``Ropen <filename>`` to start nvim with the right env vars set. This
function is now included in the updated :file:`.functions` file of these
dotfiles. It also uses the ``install-nvimcom`` function from that file.


.. details:: What these functions do

``nvimcom`` is compiled. The source code lives in this nvim plugin, but it
needs to be compiled on the R version you're using. The trick here is that
we're compiling it the first time you use it for an R version, but storing it
in a "sidecar" library.

``Ropen`` checks the
version of R on the path, pulls the source from the nvim plugin directory,
compiles nvimcom if it needs to using that R and stores it in
:file:`~/R/nvimcom/<X.Y.Z>` (that's the sidecar library), sets the
``R_LIBS_USER`` env var to point to that sidecar library, and launches R.

.. note::

R.nvim uses ``<localleader>`` instead of ``<leader>``. This essentially
allows another whole namespace to put shortcuts.

By default ``<localleader>`` is ``\\`` (backslash).

Commands that do the same thing as :ref:`toggleterm_ref` (``gx``, ``gxx``,
``,cd``, ``,k``) have been remapped to match existing toggleterm so you can
take advantage of existing muscle memory.
=======

See the docs for the plugin on how to set it up, including a macOS Shortcut.

.. list-table::
:header-rows: 1
:align: left

* - command
- description

* - ``<localleader>rf``
- Starts an R session that communicates with nvimcom

* - ``gxx`` on a line
- Send line to R (like :ref:`toggleterm_ref`)

* - ``gx`` on a selection
- Send selection to R (like :ref:`toggleterm_ref`)

* - ``<leader>cd``
- Send chunk to R (like :ref:`toggleterm_ref`)

* - ``<leader>k``
- Render RMarkdown to HTML (like :ref:`toggleterm_ref`)

* - ``<localleader>o``
- Open R object browser

* - ``<localleader>rv`` with the cursor on a dataframe variable
- Open a new nvim terminal to view the data frame in visidata. ``q``
quits visidata and the terminal.

* - ``<localleader>ro``
- Run the line under the cursor in R, capture the output, and paste it
under the cursor -- but commented out. Useful for documenting data
structures.

* - ``:RMapsDesc``
- List all the other commands possible


.. plugin-metadata::
:name: r-nvim

.. colorschemes_ref:
.. _colorschemes_ref:

Colorschemes
------------
Expand Down
Loading