Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 80
PointerAlignment: Right
SpaceAfterCStyleCast: false
SortIncludes: Never
ReflowComments: true
2 changes: 1 addition & 1 deletion lua/r/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ local check_readme = function()
-- Create or update the README (objls_ files will be regenerated if older than
-- the README).
local need_readme = false
local first_line = "Last change in this file: 2026-01-17"
local first_line = "Last change in this file: 2026-03-01"
if
vim.fn.filereadable(config.compldir .. "/README") == 0
or vim.fn.readfile(config.compldir .. "/README")[1] ~= first_line
Expand Down
51 changes: 8 additions & 43 deletions lua/r/lsp/definition.lua
Original file line number Diff line number Diff line change
Expand Up @@ -99,26 +99,6 @@ local function parse_qualified_name_at_cursor(bufnr, row, col)
return nil, word, false
end

--- Find definition in R package source
--- Communicates with nvimcom to get source location
---@param pkg string Package name
---@param symbol string Function/object name
---@param req_id string Request ID for async response
function M.find_in_package(pkg, symbol, req_id)
if vim.g.R_Nvim_status ~= 7 then
-- R is not running, can't query packages; respond immediately
utils.send_null(req_id)
return "pending"
end

-- Send request to nvimcom to get source reference
local cmd =
string.format("nvimcom:::send_definition('%s', '%s', '%s')", req_id, pkg, symbol)
require("r.run").send_to_nvimcom("E", cmd)
-- Response will be sent back asynchronously
return "pending"
end

--- Main entry point for goto definition
--- Called from rnvimserver via client/exeRnvimCmd
---@param req_id string LSP request ID
Expand Down Expand Up @@ -167,34 +147,19 @@ function M.goto_definition(req_id)
return
end

-- 3. Try package lookup if R is running
if vim.g.R_Nvim_status == 7 then
-- If qualified (pkg::fn), use that package
-- Otherwise, let nvimcom search loaded packages
-- 3. Try rnvimserver lookup
if vim.g.R_Nvim_status >= 2 then
local target_pkg = pkg or ""
M.find_in_package(target_pkg, symbol, req_id)
require("r.lsp").send_msg({
code = "G",
orig_id = req_id,
symbol = symbol,
pkg = target_pkg,
})
return
end

utils.send_null(req_id)
end

--- Handle definition response from nvimcom
--- Called when nvimcom sends back source location
---@param req_id string Request ID
---@param filepath string? File path or nil
---@param line integer? Line number (1-indexed from R)
---@param col integer? Column number
function M.handle_definition_response(req_id, filepath, line, col)
if filepath and filepath ~= "" then
utils.send_response("D", req_id, {
uri = "file://" .. filepath,
line = (line or 1) - 1, -- Convert to 0-indexed
col = (col or 1) - 1,
})
else
utils.send_null(req_id)
end
end

return M
11 changes: 7 additions & 4 deletions nvimcom/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
Package: nvimcom
Version: 0.9.86
Date: 2026-01-31
Version: 0.9.87
Date: 2026-03-01
Title: Intermediate the Communication Between R and Neovim
Author: Jakson Aquino
Maintainer: Jakson Alves de Aquino <jalvesaq@gmail.com>
Authors@R: c(
person("Jakson", "Aquino", email = "jalvesaq@gmail.com",
role = c("aut", "cre")),
person("Philippe", "Massicotte", email = "pmassicotte@hotmail.com",
role = "aut"))
Depends: R (>= 4.1.0)
Suggests: knitr, rmarkdown, quarto
Imports: methods, tools
Expand Down
6 changes: 3 additions & 3 deletions nvimcom/NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ importFrom("methods", "existsFunction", "isGeneric", "slotNames")
importFrom("graphics", "boxplot", "hist", "par", "plot")
importFrom("stats", "runif")
importFrom("utils", "browseURL", "capture.output", "example", "find",
"getAnywhere", "getS3method", "isS3stdGeneric",
"installed.packages", "methods", "packageDescription",
"read.table", "str", "Sweave", "write.table")
"getAnywhere", "getS3method", "getSrcFilename", "getSrcref",
"isS3stdGeneric", "installed.packages", "methods",
"packageDescription", "read.table", "str", "Sweave", "write.table")
importFrom("parallel", "detectCores", "mclapply")
useDynLib(nvimcom, .registration = TRUE)
29 changes: 29 additions & 0 deletions nvimcom/R/bol.R
Original file line number Diff line number Diff line change
Expand Up @@ -531,11 +531,37 @@ nvim.buildargs <- function(afile, pkg) {
return(invisible(NULL))
}

#' Build source reference cache for all functions in a package.
#' @param srcref_file Full path of the `srcref_` file to be built.
#' @param libname Library name.
nvim.build.srcref <- function(srcref_file, libname) {
packname <- paste0("package:", libname)
obj.list <- objects(packname, all.names = TRUE)
sink(srcref_file, append = FALSE)
for (obj in obj.list) {
fn <- try(get(obj, envir = as.environment(packname)), silent = TRUE)
if (inherits(fn, "try-error") || !is.function(fn)) {
next
}
sr <- getSrcref(fn)
if (is.null(sr)) {
next
}
srcfile <- getSrcFilename(sr, full.names = TRUE)
if (!nzchar(srcfile) || !file.exists(srcfile)) {
next
}
cat(obj, "\006", srcfile, "\006", sr[1], "\006", sr[5], "\n", sep = "")
}
sink()
}

#' Build data files for auto completion and for the Object Browser in the
#' cache directory:
#' - `alias_` : for finding the appropriate function during auto completion.
#' - `objls_` : for auto completion and object browser
#' - `args_` : for describing selected arguments during auto completion.
#' - `srcref_`: for source reference of functions (goto definition).
#' @param cmpllist Full path of `objls_` file to be built.
#' @param libname Library name.
nvim.bol <- function(cmpllist, libname) {
Expand Down Expand Up @@ -611,6 +637,7 @@ nvim.build.cmplls <- function() {
unlink(file.path(bdir, paste("objls", u$pkg, u$cvrs, sep = "_")))
unlink(file.path(bdir, paste("alias", u$pkg, sep = "_")))
unlink(file.path(bdir, paste("args", u$pkg, sep = "_")))
unlink(file.path(bdir, paste("srcref", u$pkg, sep = "_")))
}

# Delete outdated cache files
Expand All @@ -619,6 +646,7 @@ nvim.build.cmplls <- function() {
unlink(file.path(bdir, paste("objls", o$pkg, o$cvrs, sep = "_")))
unlink(file.path(bdir, paste("alias", o$pkg, sep = "_")))
unlink(file.path(bdir, paste("args", o$pkg, sep = "_")))
unlink(file.path(bdir, paste("srcref", o$pkg, sep = "_")))
}

# Build missing or outdated cache files
Expand All @@ -639,6 +667,7 @@ nvim.build.cmplls <- function() {
t2 <- Sys.time()
nvim.buildargs(paste0(bdir, "/args_", p), p)
t3 <- Sys.time()
nvim.build.srcref(paste0(bdir, "/srcref_", p), p)
msg <- paste0(
"INFO: ",
p,
Expand Down
2 changes: 1 addition & 1 deletion nvimcom/R/interlace.R
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ nvim.interlace.rmd <- function(Rmdfile, outform = NULL, rmddir, ...) {
} else {
if (exists("params", envir = .GlobalEnv)) {
old_params <- get("params", envir = .GlobalEnv)
rm(params, envir = .GlobalEnv)
rm("params", envir = .GlobalEnv)
}
res <- rmarkdown::render(Rmdfile, outform, ...)
if (exists("old_params", inherits = FALSE)) {
Expand Down
2 changes: 1 addition & 1 deletion nvimcom/src/apps/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CC ?= gcc
CFLAGS = -pthread -O2 -Wall
TARGET = rnvimserver
SRCS = complete.c resolve.c hover.c signature.c rhelp.c chunk.c data_structures.c logging.c rnvimserver.c obbr.c tcp.c utilities.c ../common.c
SRCS = complete.c resolve.c hover.c definition.c signature.c rhelp.c chunk.c data_structures.c logging.c rnvimserver.c obbr.c tcp.c utilities.c ../common.c

all: $(TARGET)

Expand Down
2 changes: 1 addition & 1 deletion nvimcom/src/apps/Makefile.win
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CC=gcc
TARGET=rnvimserver.exe
CFLAGS = -mwindows -std=gnu99 -O3 -Wall -DWIN32
SRCS = complete.c resolve.c hover.c signature.c rhelp.c chunk.c data_structures.c logging.c rnvimserver.c obbr.c tcp.c utilities.c ../common.c
SRCS = complete.c resolve.c hover.c definition.c signature.c rhelp.c chunk.c data_structures.c logging.c rnvimserver.c obbr.c tcp.c utilities.c ../common.c
LIBS=-lWs2_32

ifeq "$(WIN)" "64"
Expand Down
49 changes: 49 additions & 0 deletions nvimcom/src/apps/data_structures.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ static void delete_pkg(PkgData *pd) {
free(pd->objls);
if (pd->args)
free(pd->args);
if (pd->srcref)
free(pd->srcref);
if (pd->title) // free title, descr and alias
free(pd->title);
free(pd);
Expand Down Expand Up @@ -194,6 +196,52 @@ static void *read_alias_file(PkgData *pd) {
return b;
}

static char *read_srcref_file(const char *nm) {
char fnm[512];
snprintf(fnm, 511, "%s/srcref_%s", cmp_dir, nm);
char *b = read_file(fnm, 0);
if (!b)
return NULL;

int size = strlen(b);
if (size == 0)
return b;

// Validate: expect exactly 3 \006 per line
const char *s0 = b;
char *s1 = b;
int n = 0;
while (*s1) {
if (*s1 == '\006')
n++;
if (*s1 == '\n') {
if (n == 3) {
n = 0;
s0 = s1 + 1;
} else {
char buf[64];
strncpy(buf, s0, 63);
buf[63] = '\0';
fprintf(stderr, "srcref: bad separator count: %d (%s)\n", n,
buf);
fflush(stderr);
free(b);
return NULL;
}
}
s1++;
}

// Convert \006 to \0
char *p = b;
while (*p) {
if (*p == '\006')
*p = 0;
p++;
}
return b;
}

static char *read_args_file(const char *nm) {
char fnm[512];
snprintf(fnm, 511, "%s/args_%s", cmp_dir, nm);
Expand All @@ -214,6 +262,7 @@ static void load_pkg_data(PkgData *pd, const char *fname) {
int size;
read_alias_file(pd);
pd->args = read_args_file(pd->name);
pd->srcref = read_srcref_file(pd->name);
if (!pd->objls) {
pd->nobjs = 0;
pd->objls = read_objls_file(fname, &size);
Expand Down
1 change: 1 addition & 0 deletions nvimcom/src/apps/data_structures.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ typedef struct pkg_data_ {
char *alias; // A copy of the alias_ file
char *objls; // A copy of the objls_ file
char *args; // A copy of the args_ file
char *srcref; // A copy of the srcref_ file (source references)
int nobjs; // Number of objects in objls
} PkgData;

Expand Down
Loading