From bbee2647311da631acba5507d3ac2ac79ac262d5 Mon Sep 17 00:00:00 2001 From: Philippe Massicotte Date: Tue, 24 Feb 2026 14:19:12 -0500 Subject: [PATCH 1/8] refactor(lsp/definition.lua): remove redundant R package source lookup functions (closes #508) feat(bol.R): add source reference cache building for package functions This change introduces a new function `nvim.build.srcref` to build source reference caches for functions in R packages, enhancing the goto definition feature. fix(Makefile): include definition.c in source files for build This ensures that the new definition handling logic is compiled into the rnvimserver application. fix(data_structures.c): add srcref file reading and validation This change allows the application to read and validate source reference files, supporting the new goto definition functionality. fix(data_structures.h): add srcref field to PkgData struct This addition supports storing source reference data for R packages. feat(definition): add symbol definition resolution feature Introduce a new feature to resolve symbol definitions using cached data or fallback to R for lookup. This enhances the ability to locate symbols efficiently within packages, improving the overall functionality of the application. --- lua/r/lsp/definition.lua | 51 ++------- nvimcom/R/bol.R | 23 ++++ nvimcom/src/apps/Makefile | 2 +- nvimcom/src/apps/Makefile.win | 2 +- nvimcom/src/apps/data_structures.c | 49 ++++++++ nvimcom/src/apps/data_structures.h | 1 + nvimcom/src/apps/definition.c | 175 +++++++++++++++++++++++++++++ nvimcom/src/apps/definition.h | 6 + nvimcom/src/apps/rnvimserver.c | 4 + 9 files changed, 268 insertions(+), 45 deletions(-) create mode 100644 nvimcom/src/apps/definition.c create mode 100644 nvimcom/src/apps/definition.h diff --git a/lua/r/lsp/definition.lua b/lua/r/lsp/definition.lua index d346da6d..cc0848c0 100644 --- a/lua/r/lsp/definition.lua +++ b/lua/r/lsp/definition.lua @@ -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 @@ -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 diff --git a/nvimcom/R/bol.R b/nvimcom/R/bol.R index 21399263..36af2bf2 100644 --- a/nvimcom/R/bol.R +++ b/nvimcom/R/bol.R @@ -531,11 +531,31 @@ 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) { @@ -611,6 +631,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, u$cvrs, sep = "_"))) } # Delete outdated cache files @@ -619,6 +640,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, o$cvrs, sep = "_"))) } # Build missing or outdated cache files @@ -639,6 +661,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, "_", pvi), p) msg <- paste0( "INFO: ", p, diff --git a/nvimcom/src/apps/Makefile b/nvimcom/src/apps/Makefile index a2f71a70..aa986f5e 100644 --- a/nvimcom/src/apps/Makefile +++ b/nvimcom/src/apps/Makefile @@ -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) diff --git a/nvimcom/src/apps/Makefile.win b/nvimcom/src/apps/Makefile.win index a88f6629..83e7a80d 100644 --- a/nvimcom/src/apps/Makefile.win +++ b/nvimcom/src/apps/Makefile.win @@ -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" diff --git a/nvimcom/src/apps/data_structures.c b/nvimcom/src/apps/data_structures.c index 46328607..70a9dc87 100644 --- a/nvimcom/src/apps/data_structures.c +++ b/nvimcom/src/apps/data_structures.c @@ -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); @@ -194,6 +196,52 @@ static void *read_alias_file(PkgData *pd) { return b; } +static char *read_srcref_file(const char *nm, const char *vrsn) { + char fnm[512]; + snprintf(fnm, 511, "%s/srcref_%s_%s", cmp_dir, nm, vrsn); + 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); @@ -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, pd->version); if (!pd->objls) { pd->nobjs = 0; pd->objls = read_objls_file(fname, &size); diff --git a/nvimcom/src/apps/data_structures.h b/nvimcom/src/apps/data_structures.h index b109e794..50223e66 100644 --- a/nvimcom/src/apps/data_structures.h +++ b/nvimcom/src/apps/data_structures.h @@ -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; diff --git a/nvimcom/src/apps/definition.c b/nvimcom/src/apps/definition.c new file mode 100644 index 00000000..cb4d43fb --- /dev/null +++ b/nvimcom/src/apps/definition.c @@ -0,0 +1,175 @@ +#include +#include +#include + +#include "definition.h" +#include "global_vars.h" +#include "logging.h" +#include "lsp.h" +#include "utilities.h" +#include "tcp.h" + +/** + * @brief Search a package's srcref buffer for a symbol and extract + * file path, line, and column. + * + * The srcref buffer has the format (with \0 as separator, converted from \006): + * funcname\0filepath\0line\0col\n + * + * @param srcref The srcref buffer. + * @param symbol The symbol name to find. + * @param file Output: pointer to file path string within the buffer. + * @param line Output: line number (1-indexed from R). + * @param col Output: column number. + * @return 1 if found, 0 otherwise. + */ +static int seek_srcref(const char *srcref, const char *symbol, + const char **file, int *line, int *col) { + const char *s = srcref; + while (*s) { + if (strcmp(s, symbol) == 0) { + while (*s) + s++; + s++; + *file = s; + while (*s) + s++; + s++; + *line = atoi(s); + while (*s) + s++; + s++; + *col = atoi(s); + return 1; + } + while (*s != '\n') + s++; + s++; + } + return 0; +} + +static void send_definition_location(const char *req_id, const char *filepath, + int line, int col) { + const char *fmt = + "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":" + "{\"uri\":\"file://%s\",\"range\":{\"start\":" + "{\"line\":%d,\"character\":%d},\"end\":" + "{\"line\":%d,\"character\":%d}}}}"; + + size_t len = strlen(filepath) + strlen(req_id) + 256; + char *res = (char *)malloc(len); + int lsp_line = line - 1; + if (lsp_line < 0) + lsp_line = 0; + snprintf(res, len - 1, fmt, req_id, filepath, lsp_line, col, lsp_line, + col); + send_ls_response(req_id, res); + free(res); +} + +/** + * @brief Try to resolve a symbol's definition from a package's cached data. + * + * If the srcref cache has a hit, sends the location directly. + * If the srcref cache was built but has no entry, falls back to R with the + * specific package name (fast single-namespace lookup). + * If no srcref cache exists, falls back to R with the specific package name. + * + * @return 1 if handled (response sent or R fallback dispatched), 0 otherwise. + */ +static int try_resolve(const char *id, const char *symbol, + const char *pkg_name, PkgData *pkg) { + const char *file; + int line, col; + + if (pkg->srcref && + seek_srcref(pkg->srcref, symbol, &file, &line, &col)) { + send_definition_location(id, file, line, col); + return 1; + } + if (r_running) { + char cmd[512]; + snprintf(cmd, 511, "nvimcom:::send_definition('%s', '%s', '%s')", id, + pkg_name, symbol); + nvimcom_eval(cmd); + return 1; + } + send_null(id); + return 1; +} + +void definition(const char *params) { + Log("definition: %s", params); + + char *id = strstr(params, "\"orig_id\":"); + char *symbol = strstr(params, "\"symbol\":\""); + char *pkg = strstr(params, "\"pkg\":\""); + + cut_json_int(&id, 10); + cut_json_str(&symbol, 10); + cut_json_str(&pkg, 7); + + if (!id || !symbol || !*symbol) { + if (id) + send_null(id); + return; + } + + if (pkg && *pkg) { + LibList *lib = inst_libs; + while (lib) { + if (strcmp(lib->pkg->name, pkg) == 0) { + try_resolve(id, symbol, pkg, lib->pkg); + return; + } + lib = lib->next; + } + if (r_running) { + char cmd[512]; + snprintf(cmd, 511, + "nvimcom:::send_definition('%s', '%s', '%s')", id, pkg, + symbol); + nvimcom_eval(cmd); + return; + } + send_null(id); + return; + } + + LibList *lib = loaded_libs; + while (lib) { + if (lib->pkg->objls) { + const char *s = seek_word(lib->pkg->objls, symbol); + if (s) { + try_resolve(id, symbol, lib->pkg->name, lib->pkg); + return; + } + } + lib = lib->next; + } + + // Not in loaded_libs — search inst_libs + lib = inst_libs; + while (lib) { + if (lib->pkg->objls) { + const char *s = seek_word(lib->pkg->objls, symbol); + if (s) { + try_resolve(id, symbol, lib->pkg->name, lib->pkg); + return; + } + } + lib = lib->next; + } + + // Not found — full R search as last resort + if (r_running) { + char cmd[512]; + snprintf(cmd, 511, "nvimcom:::send_definition('%s', '', '%s')", id, + symbol); + nvimcom_eval(cmd); + return; + } + + send_null(id); +} diff --git a/nvimcom/src/apps/definition.h b/nvimcom/src/apps/definition.h new file mode 100644 index 00000000..8b5a4cd8 --- /dev/null +++ b/nvimcom/src/apps/definition.h @@ -0,0 +1,6 @@ +#ifndef DEFINITION_H +#define DEFINITION_H + +void definition(const char *params); + +#endif diff --git a/nvimcom/src/apps/rnvimserver.c b/nvimcom/src/apps/rnvimserver.c index c07aefff..a8e0e256 100644 --- a/nvimcom/src/apps/rnvimserver.c +++ b/nvimcom/src/apps/rnvimserver.c @@ -9,6 +9,7 @@ #include "complete.h" #include "resolve.h" #include "hover.h" +#include "definition.h" #include "signature.h" #include "tcp.h" #include "obbr.h" @@ -299,6 +300,9 @@ static void handle_exe_cmd(const char *params) { case 'H': hover(params); break; + case 'G': + definition(params); + break; case 'S': signature(params); break; From 92996511239560106011fd65b6c6972379032bc9 Mon Sep 17 00:00:00 2001 From: Philippe Massicotte Date: Wed, 25 Feb 2026 09:42:08 -0500 Subject: [PATCH 2/8] style: apply consistent formatting and line breaks in C source files Add .clang-format file to enforce code style based on LLVM style. Adjust line breaks and indentation in C files for better readability. I ran clang-format on all c files for consistency. In my `conform.lua`, I have this now: ```lua clang_format = {}, ``` --- .clang-format | 8 ++++++ nvimcom/src/apps/definition.c | 24 ++++++++---------- nvimcom/src/apps/rnvimserver.c | 45 ++++++++++++++++++++++------------ nvimcom/src/apps/tcp.c | 18 +++++++++----- 4 files changed, 59 insertions(+), 36 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..dd6d453f --- /dev/null +++ b/.clang-format @@ -0,0 +1,8 @@ +--- +BasedOnStyle: LLVM +IndentWidth: 4 +ColumnLimit: 80 +PointerAlignment: Right +SpaceAfterCStyleCast: false +SortIncludes: Never +ReflowComments: true diff --git a/nvimcom/src/apps/definition.c b/nvimcom/src/apps/definition.c index cb4d43fb..83a3b1be 100644 --- a/nvimcom/src/apps/definition.c +++ b/nvimcom/src/apps/definition.c @@ -51,19 +51,17 @@ static int seek_srcref(const char *srcref, const char *symbol, static void send_definition_location(const char *req_id, const char *filepath, int line, int col) { - const char *fmt = - "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":" - "{\"uri\":\"file://%s\",\"range\":{\"start\":" - "{\"line\":%d,\"character\":%d},\"end\":" - "{\"line\":%d,\"character\":%d}}}}"; + const char *fmt = "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":" + "{\"uri\":\"file://%s\",\"range\":{\"start\":" + "{\"line\":%d,\"character\":%d},\"end\":" + "{\"line\":%d,\"character\":%d}}}}"; size_t len = strlen(filepath) + strlen(req_id) + 256; char *res = (char *)malloc(len); int lsp_line = line - 1; if (lsp_line < 0) lsp_line = 0; - snprintf(res, len - 1, fmt, req_id, filepath, lsp_line, col, lsp_line, - col); + snprintf(res, len - 1, fmt, req_id, filepath, lsp_line, col, lsp_line, col); send_ls_response(req_id, res); free(res); } @@ -78,13 +76,12 @@ static void send_definition_location(const char *req_id, const char *filepath, * * @return 1 if handled (response sent or R fallback dispatched), 0 otherwise. */ -static int try_resolve(const char *id, const char *symbol, - const char *pkg_name, PkgData *pkg) { +static int try_resolve(const char *id, const char *symbol, const char *pkg_name, + PkgData *pkg) { const char *file; int line, col; - if (pkg->srcref && - seek_srcref(pkg->srcref, symbol, &file, &line, &col)) { + if (pkg->srcref && seek_srcref(pkg->srcref, symbol, &file, &line, &col)) { send_definition_location(id, file, line, col); return 1; } @@ -127,9 +124,8 @@ void definition(const char *params) { } if (r_running) { char cmd[512]; - snprintf(cmd, 511, - "nvimcom:::send_definition('%s', '%s', '%s')", id, pkg, - symbol); + snprintf(cmd, 511, "nvimcom:::send_definition('%s', '%s', '%s')", + id, pkg, symbol); nvimcom_eval(cmd); return; } diff --git a/nvimcom/src/apps/rnvimserver.c b/nvimcom/src/apps/rnvimserver.c index a8e0e256..c006fbaf 100644 --- a/nvimcom/src/apps/rnvimserver.c +++ b/nvimcom/src/apps/rnvimserver.c @@ -459,7 +459,8 @@ static void handle_implementation(const char *id) { send_cmd_to_nvim(i_cmd); } -// Generic function to handle location-based LSP responses (definition, references, implementation) +// Generic function to handle location-based LSP responses (definition, +// references, implementation) static void send_location_result(const char *params) { // IMPORTANT: Search for ALL fields BEFORE calling cut_json_* functions, // because those functions NULL-terminate and modify the params string! @@ -486,22 +487,26 @@ static void send_location_result(const char *params) { size_t result_size = 4096; char *result = (char *)malloc(result_size); char *p = result; - p += snprintf(p, result_size, "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":[", id); + p += snprintf(p, result_size, + "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":[", id); char *loc = arr_start + 1; int first = 1; while (loc < arr_end) { char *obj_start = strchr(loc, '{'); - if (!obj_start || obj_start >= arr_end) break; + if (!obj_start || obj_start >= arr_end) + break; char *obj_end = strchr(obj_start, '}'); - if (!obj_end || obj_end > arr_end) break; + if (!obj_end || obj_end > arr_end) + break; char *file = strstr(obj_start, "\"file\":\""); char *line = strstr(obj_start, "\"line\":"); char *col = strstr(obj_start, "\"col\":"); - if (file && line && col && file < obj_end && line < obj_end && col < obj_end) { + if (file && line && col && file < obj_end && line < obj_end && + col < obj_end) { file += 8; char *file_end = strchr(file, '"'); if (file_end && file_end < obj_end) { @@ -521,9 +526,12 @@ static void send_location_result(const char *params) { first = 0; p += snprintf(p, result_size - (p - result), - "{\"uri\":\"file://%s\",\"range\":{\"start\":{\"line\":%d,\"character\":%d}," - "\"end\":{\"line\":%d,\"character\":%d}}}", - file_str, line_num, col_num, line_num, col_num); + "{\"uri\":\"file://" + "%s\",\"range\":{\"start\":{\"line\":%d," + "\"character\":%d}," + "\"end\":{\"line\":%d,\"character\":%d}}}", + file_str, line_num, col_num, line_num, + col_num); free(file_str); } @@ -545,14 +553,16 @@ static void send_location_result(const char *params) { cut_json_int(&col_field, 6); // Build the LSP Location response - const char *fmt = - "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":" - "{\"uri\":\"%s\",\"range\":{\"start\":{\"line\":%s,\"character\":%s}," - "\"end\":{\"line\":%s,\"character\":%s}}}}"; + const char *fmt = "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":" + "{\"uri\":\"%s\",\"range\":{\"start\":{\"line\":%s," + "\"character\":%s}," + "\"end\":{\"line\":%s,\"character\":%s}}}}"; - size_t len = strlen(uri) + strlen(id) + strlen(line_field) * 2 + strlen(col_field) * 2 + 256; + size_t len = strlen(uri) + strlen(id) + strlen(line_field) * 2 + + strlen(col_field) * 2 + 256; char *res = (char *)malloc(len); - snprintf(res, len - 1, fmt, id, uri, line_field, col_field, line_field, col_field); + snprintf(res, len - 1, fmt, id, uri, line_field, col_field, line_field, + col_field); send_ls_response(id, res); free(res); } @@ -585,7 +595,8 @@ static void send_document_symbols_result(const char *params) { return; } - // Build the result - we'll pass through the symbols array as-is since Lua already formatted it correctly + // Build the result - we'll pass through the symbols array as-is since Lua + // already formatted it correctly. // The Lua code sends DocumentSymbol objects with all required fields size_t result_size = (arr_end - arr_start) + 256; char *result = (char *)malloc(result_size); @@ -596,7 +607,9 @@ static void send_document_symbols_result(const char *params) { strncpy(array_content, arr_start, array_len); array_content[array_len] = '\0'; - snprintf(result, result_size, "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":%s}", id, array_content); + snprintf(result, result_size, + "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":%s}", id, + array_content); send_ls_response(id, result); free(array_content); diff --git a/nvimcom/src/apps/tcp.c b/nvimcom/src/apps/tcp.c index 2a7f4be3..1e580972 100644 --- a/nvimcom/src/apps/tcp.c +++ b/nvimcom/src/apps/tcp.c @@ -138,7 +138,8 @@ static void ParseMsg(char *b) { "{\"line\":%d,\"character\":%d}}}}"; size_t len = strlen(def_file) + strlen(def_id) + 256; char *res = (char *)malloc(len); - snprintf(res, len - 1, fmt, def_id, def_file, line, col, line, col); + snprintf(res, len - 1, fmt, def_id, def_file, line, col, + line, col); send_ls_response(def_id, res); free(res); } @@ -162,18 +163,21 @@ static void ParseMsg(char *b) { char *result = (char *)malloc(result_size); char *p = result; p += snprintf(p, result_size, - "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":[", multi_id); + "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":[", + multi_id); for (int i = 0; i < count; i++) { char *m_file = b; b = strstr(b, "|"); - if (!b) break; + if (!b) + break; *b = '\0'; b++; char *m_line_str = b; b = strstr(b, "|"); - if (!b) break; + if (!b) + break; *b = '\0'; b++; @@ -184,13 +188,15 @@ static void ParseMsg(char *b) { b = next + 1; } - int m_line = atoi(m_line_str) - 1; // 1-indexed to 0-indexed + int m_line = + atoi(m_line_str) - 1; // 1-indexed to 0-indexed int m_col = atoi(m_col_str); if (i > 0) { p += snprintf(p, result_size - (p - result), ","); } - p += snprintf(p, result_size - (p - result), + p += snprintf( + p, result_size - (p - result), "{\"uri\":\"file://%s\",\"range\":{\"start\":" "{\"line\":%d,\"character\":%d},\"end\":" "{\"line\":%d,\"character\":%d}}}", From 4b06caf5c96c39371be47eb792f62c8ff2eefcd6 Mon Sep 17 00:00:00 2001 From: Philippe Massicotte Date: Wed, 25 Feb 2026 09:51:43 -0500 Subject: [PATCH 3/8] style(bol.R): run air formatter --- nvimcom/R/bol.R | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/nvimcom/R/bol.R b/nvimcom/R/bol.R index 36af2bf2..a80233d2 100644 --- a/nvimcom/R/bol.R +++ b/nvimcom/R/bol.R @@ -540,11 +540,17 @@ nvim.build.srcref <- function(srcref_file, libname) { 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 + if (inherits(fn, "try-error") || !is.function(fn)) { + next + } sr <- getSrcref(fn) - if (is.null(sr)) next + if (is.null(sr)) { + next + } srcfile <- getSrcFilename(sr, full.names = TRUE) - if (!nzchar(srcfile) || !file.exists(srcfile)) next + if (!nzchar(srcfile) || !file.exists(srcfile)) { + next + } cat(obj, "\006", srcfile, "\006", sr[1], "\006", sr[5], "\n", sep = "") } sink() From 9747e0ec5e4f016e91527fa826ddbebd7baaea39 Mon Sep 17 00:00:00 2001 From: Philippe Massicotte Date: Sun, 1 Mar 2026 09:06:12 -0500 Subject: [PATCH 4/8] chore(config.lua): update README last change date to 2026-03-01 chore(nvimcom/DESCRIPTION): bump version to 0.9.87 and update date to 2026-03-01 --- lua/r/config.lua | 2 +- nvimcom/DESCRIPTION | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/r/config.lua b/lua/r/config.lua index f65e6749..7b04f9a3 100644 --- a/lua/r/config.lua +++ b/lua/r/config.lua @@ -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 diff --git a/nvimcom/DESCRIPTION b/nvimcom/DESCRIPTION index d675fe3b..29d40ec6 100644 --- a/nvimcom/DESCRIPTION +++ b/nvimcom/DESCRIPTION @@ -1,6 +1,6 @@ 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 From bce650707651ef1cef31e66aabbbf81df2551153 Mon Sep 17 00:00:00 2001 From: Philippe Massicotte Date: Sun, 1 Mar 2026 09:09:36 -0500 Subject: [PATCH 5/8] refactor(bol.R, data_structures.c): remove version from srcref file handling --- nvimcom/R/bol.R | 6 +++--- nvimcom/src/apps/data_structures.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nvimcom/R/bol.R b/nvimcom/R/bol.R index a80233d2..a42ec600 100644 --- a/nvimcom/R/bol.R +++ b/nvimcom/R/bol.R @@ -637,7 +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, u$cvrs, sep = "_"))) + unlink(file.path(bdir, paste("srcref", u$pkg, sep = "_"))) } # Delete outdated cache files @@ -646,7 +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, o$cvrs, sep = "_"))) + unlink(file.path(bdir, paste("srcref", o$pkg, sep = "_"))) } # Build missing or outdated cache files @@ -667,7 +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, "_", pvi), p) + nvim.build.srcref(paste0(bdir, "/srcref_", p), p) msg <- paste0( "INFO: ", p, diff --git a/nvimcom/src/apps/data_structures.c b/nvimcom/src/apps/data_structures.c index 70a9dc87..15c4809a 100644 --- a/nvimcom/src/apps/data_structures.c +++ b/nvimcom/src/apps/data_structures.c @@ -196,9 +196,9 @@ static void *read_alias_file(PkgData *pd) { return b; } -static char *read_srcref_file(const char *nm, const char *vrsn) { +static char *read_srcref_file(const char *nm) { char fnm[512]; - snprintf(fnm, 511, "%s/srcref_%s_%s", cmp_dir, nm, vrsn); + snprintf(fnm, 511, "%s/srcref_%s", cmp_dir, nm); char *b = read_file(fnm, 0); if (!b) return NULL; @@ -262,7 +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, pd->version); + pd->srcref = read_srcref_file(pd->name); if (!pd->objls) { pd->nobjs = 0; pd->objls = read_objls_file(fname, &size); From 02f9b36a307d6f23839a9a7e945d02c017612ba7 Mon Sep 17 00:00:00 2001 From: Philippe Massicotte Date: Sun, 1 Mar 2026 09:23:09 -0500 Subject: [PATCH 6/8] docs(DESCRIPTION): update author information --- nvimcom/DESCRIPTION | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nvimcom/DESCRIPTION b/nvimcom/DESCRIPTION index 29d40ec6..8a591164 100644 --- a/nvimcom/DESCRIPTION +++ b/nvimcom/DESCRIPTION @@ -2,8 +2,11 @@ Package: nvimcom Version: 0.9.87 Date: 2026-03-01 Title: Intermediate the Communication Between R and Neovim -Author: Jakson Aquino -Maintainer: Jakson Alves de Aquino +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 From 9ab78dfd0ccc4aa08943895acb1b76496e5eb0fb Mon Sep 17 00:00:00 2001 From: Philippe Massicotte Date: Sun, 1 Mar 2026 11:40:53 -0500 Subject: [PATCH 7/8] fix(interlace.R): change rm function to use string for parameter name Use a string in the rm function to avoid potential issues with variable name resolution and improve code clarity. --- nvimcom/R/interlace.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nvimcom/R/interlace.R b/nvimcom/R/interlace.R index 1d0aa2c1..6029afda 100644 --- a/nvimcom/R/interlace.R +++ b/nvimcom/R/interlace.R @@ -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)) { From 99b6c91c570502c63abd1aa81ce84c2191d5f691 Mon Sep 17 00:00:00 2001 From: Philippe Massicotte Date: Sun, 1 Mar 2026 11:43:00 -0500 Subject: [PATCH 8/8] feat(NAMESPACE): add getSrcFilename and getSrcref to utils imports --- nvimcom/NAMESPACE | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nvimcom/NAMESPACE b/nvimcom/NAMESPACE index e5d68324..785ca34c 100644 --- a/nvimcom/NAMESPACE +++ b/nvimcom/NAMESPACE @@ -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)