Skip to content

Commit

Permalink
Improvements when building completion data
Browse files Browse the repository at this point in the history
- New options to control how many list levels to include in completion data: `compl_data`.
- Decrease the default number of levels to include in the completion data (Close #142).
- Correctly get the last unnamed list element (Close #128).
  • Loading branch information
jalvesaq authored Jun 15, 2024
1 parent 27dccbe commit dc4eb50
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 33 deletions.
43 changes: 41 additions & 2 deletions doc/R.nvim.txt
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ command:
|objbr_opendf| Display data.frames open in the Object Browser
|objbr_openlist| Display lists open in the Object Browser
|objbr_allnames| Display hidden objects in the Object Browser
|compl_data| Limits to completion data (avoid R slowdown)
|nvimpager| Use Neovim to see R documentation
|open_example| Use Neovim to display R examples
|R_path| Directory where R is
Expand Down Expand Up @@ -995,13 +996,13 @@ cursor is not at the beginning of the line.


------------------------------------------------------------------------------
6.7. Object Browser options *objbr_w*
6.7. Object Browser and completion options *objbr_w*
*objbr_h*
*objbr_place*
*objbr_opendf*
*objbr_openlist*
*objbr_allnames*

*compl_data*
By default, the Object Browser will be created at the right of the script
window, and with 40 columns. Valid values for the Object Browser placement are
the combination of either "script" or "console" and "right", "left", "above",
Expand Down Expand Up @@ -1042,6 +1043,44 @@ only if it is explicitly opened. The options `objbr_opendf` and
respectively, `data.frames` and `lists`. The options are ignored for
`data.frames` and `lists` of libraries which are always started closed.

When building the list of objects for auto completion and for the Object
Browser, `nvimcom` inspects lists only as deep as 3 levels. When the time to
build the completion data is higher than 100 ms `nvimcom` decreases the number
of levels it inspects in lists. Even if `nvimcom` can build the completion
data in less than 100 ms, it also decrease the maximum level of list
inspection if the resulting completion data is bigger than 1,000,000 bytes
because big data may take a noticeably long time to be transmitted through the
TCP connection. When `nvimcom` automatically decreases the depth of list
inspection, if `nvimcom.verbose` is > 1, it outputs information like the
following on R Console:

nvimcom:
Time to buiild list of objects: 7.582 ms (max_time = 100 ms)
List size: 1611734 bytes (max_size = 1000000 bytes)
New max_depth: 9

The value of `max_depth` will automatically increase if necessary when you try
to open a list in the Object Browser. However, it will not increase if you try
to complete deep elements in the list.

You can set different values for `time_limit`, `size_limit`, and `max_depth`
in the Lua table `compl_data` in your `R.nvim` config. Below are the default
values:
>lua
compl_data = {
max_depth = 3,
max_size = 1000000,
max_time = 100,
},
<
You should increase the value of `max_depth` if you want to complete or see in
the Object Browser more than 3 list levels. You should increase either
`max_size` or `max_time` if `nvimcom` needs more space or more time to build
the completion data with the desired list depth. You should decrease the
values if you notice any delay when R is running commands. In this case,
please, put `options(nvimcom.verbose = 1)` in your `~/.Rprofile` and use the
information output by `nvimcom` to decide what parameter to change.


------------------------------------------------------------------------------
6.8. Neovim as pager for R *open_example*
Expand Down
5 changes: 5 additions & 0 deletions lua/r/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ local config = {
clear_line = false,
close_term = true,
compldir = "",
compl_data = {
max_depth = 3,
max_size = 1000000,
max_time = 100,
},
config_tmux = true,
csv_app = "",
disable_cmds = { "" },
Expand Down
12 changes: 12 additions & 0 deletions lua/r/run.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ start_R2 = function()
'Sys.setenv("R_DEFAULT_PACKAGES" = "' .. rdp .. '")',
}

table.insert(
start_options,
"options(nvimcom.max_depth = " .. tostring(config.compl_data.max_depth) .. ")"
)
table.insert(
start_options,
"options(nvimcom.max_size = " .. tostring(config.compl_data.max_size) .. ")"
)
table.insert(
start_options,
"options(nvimcom.max_time = " .. tostring(config.compl_data.max_time) .. ")"
)
if config.objbr_allnames then
table.insert(start_options, "options(nvimcom.allnames = TRUE)")
else
Expand Down
1 change: 1 addition & 0 deletions lua/r/server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ local start_rnvimserver = function()
if config.objbr_allnames then rns_env["RNVIM_OBJBR_ALLNAMES"] = "TRUE" end
rns_env["RNVIM_RPATH"] = config.R_cmd
rns_env["RNVIM_LOCAL_TMPDIR"] = config.localtmpdir
rns_env["RNVIM_MAX_DEPTH"] = tostring(config.compl_data.max_depth)

-- We have to set R's home directory on Windows because rnvimserver will
-- run R to build the list for auto completion.
Expand Down
4 changes: 2 additions & 2 deletions nvimcom/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: nvimcom
Version: 0.9.41
Date: 2024-04-05
Version: 0.9.42
Date: 2024-06-14
Title: Intermediate the Communication Between R and Neovim
Author: Jakson Aquino
Maintainer: Jakson Alves de Aquino <[email protected]>
Expand Down
6 changes: 6 additions & 0 deletions nvimcom/R/nvimcom.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ NvimcomEnv$pkgRdDB <- list()
options(nvimcom.setwidth = TRUE)
options(nvimcom.autoglbenv = 0)
options(nvimcom.nvimpager = TRUE)
options(nvimcom.max_depth = 12)
options(nvimcom.max_size = 1000000)
options(nvimcom.max_time = 100)
options(nvimcom.delim = "\t")
}
if (getOption("nvimcom.nvimpager"))
Expand All @@ -51,6 +54,9 @@ NvimcomEnv$pkgRdDB <- list()
as.integer(getOption("nvimcom.allnames")),
as.integer(getOption("nvimcom.setwidth")),
as.integer(getOption("nvimcom.autoglbenv")),
as.integer(getOption("nvimcom.max_depth")),
as.integer(getOption("nvimcom.max_size")),
as.integer(getOption("nvimcom.max_time")),
NvimcomEnv$info[1],
NvimcomEnv$info[2],
PACKAGE = "nvimcom")
Expand Down
26 changes: 24 additions & 2 deletions nvimcom/src/apps/data_structures.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
#include "../common.h"
#include "logging.h"
#include "data_structures.h"
#include "tcp.h"

static int building_objls; // Flag for building compl lists
static int more_to_build; // Flag for more lists to build
static LibPath *libpaths; // Pointer to first library path
static size_t glbnv_buffer_sz; // Global environment buffer size
static ListStatus *listTree; // Root node of the list status tree
static int max_depth = 2; // Max list depth in nvimcom

void set_max_depth(int m) { max_depth = m; }

static void change_all_stt(ListStatus *root, int stt) {
if (root != NULL) {
Expand Down Expand Up @@ -574,10 +578,28 @@ int get_list_status(const char *s, int stt) {
* Description:
* @param s:
*/
void toggle_list_status(const char *s) {
void toggle_list_status(char *s) {
ListStatus *p = search(listTree, s);
if (p)
if (p) {

// Count list levels
char *t = s;
int n = 0;
while (*t) {
if (*t == '$' || *t == '@')
n++;
t++;
}
// Check if the value of max_depth is high enough
if (p->status == 0 && n >= max_depth) {
max_depth++;
char b[16];
snprintf(b, 15, "D%d", n + 1);
send_to_nvimcom(b);
}

p->status = !p->status;
}
}

/**
Expand Down
3 changes: 2 additions & 1 deletion nvimcom/src/apps/data_structures.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ typedef struct pkg_data_ {
struct pkg_data_ *next; // Pointer to next package data
} PkgData;

void set_max_depth(int m);
int get_list_status(const char *s, int stt);
void toggle_list_status(const char *s);
void toggle_list_status(char *s);
void update_inst_libs(void);
void update_pkg_list(char *libnms); // Update package list
void update_glblenv_buffer(char *g); // Update global environment buffer
Expand Down
1 change: 1 addition & 0 deletions nvimcom/src/apps/rnvimserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ static void init_global_vars(void) {
} else {
strncpy(localtmpdir, getenv("RNVIM_TMPDIR"), 255);
}
set_max_depth(atoi(getenv("RNVIM_MAX_DEPTH")));

compl_buffer = calloc(compl_buffer_size, sizeof(char));
}
Expand Down
4 changes: 4 additions & 0 deletions nvimcom/src/apps/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ static void ParseMsg(char *b) {
else
complete(id, base, fnm, dtfrm, NULL);
break;
case 'D': // set max_depth of lists in the completion data
b++;
set_max_depth(atoi(b));
break;
case 'F': // strtok doesn't work here because "base" might be empty.
b++;
char *args;
Expand Down
62 changes: 36 additions & 26 deletions nvimcom/src/nvimcom.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,19 @@ static size_t tcp_header_len; // Length of nvimsecr + 9. Stored in a
// variable to avoid repeatedly calling
// strlen().

static int maxdepth = 6; // How many levels to parse in lists and S4 objects
static double timelimit =
100.0; // Maximum acceptable time to build list of .GlobalEnv objects
static int sizelimit = 1000000; // Maximum acceptable size of string
// representing .GlobalEnv (list of objects)
static int maxdepth = 12; // How many levels to parse in lists and S4 objects
// when building list of objects for auto-completion. The value decreases if
// the listing is too slow and increases if there are more levels to be parsed
// and the listing is fast enough.
// the listing is too slow.
static int curdepth = 0; // Current level of the list or S4 object being parsed
// for auto-completion.
static int autoglbenv = 0; // Should the list of objects in .GlobalEnv be
// automatically updated after each top level command is executed? It will
// always be 2 if cmp-r is installed; otherwise, it will be 1 if the Object
// Browser is open.
static clock_t tm; // Time when the listing of objects from .GlobalEnv started.

static char tmpdir[512]; // The environment variable RNVIM_TMPDIR.
static int setwidth = 0; // Set the option width after each command is executed
Expand Down Expand Up @@ -571,21 +573,6 @@ static char *nvimcom_glbnv_line(SEXP *x, const char *xname, const char *curenv,
char newenv[576];
SEXP elmt = R_NilValue;
const char *ename;
double tmdiff = 1000 * ((double)clock() - tm) / CLOCKS_PER_SEC;
if (tmdiff > 300.0) {
maxdepth = curdepth;
if (verbose > 3)
REprintf("nvimcom: slow at building list of objects (%g ms); "
"maxdepth = %d\n",
tmdiff, maxdepth);
return p;
} else if (tmdiff < 100.0 && maxdepth <= curdepth) {
maxdepth++;
if (verbose > 3)
REprintf("nvimcom: increased maxdepth to %d (time to build "
"completion data = %g)\n",
maxdepth, tmdiff);
}

if (xgroup == 4) {
snprintf(newenv, 575, "%s%s@", curenv, xname);
Expand Down Expand Up @@ -615,7 +602,7 @@ static char *nvimcom_glbnv_line(SEXP *x, const char *xname, const char *curenv,
depth + 1);
}
snprintf(ebuf, 63, "[[%d]]", len1 + 1);
PROTECT(elmt = VECTOR_ELT(*x, len));
PROTECT(elmt = VECTOR_ELT(*x, len1));
p = nvimcom_glbnv_line(&elmt, ebuf, newenv, p, depth + 1);
UNPROTECT(1);
}
Expand Down Expand Up @@ -685,7 +672,7 @@ static void nvimcom_globalenv_list(void) {
if (tmpdir[0] == 0)
return;

tm = clock();
double tm = clock();

memset(glbnvbuf2, 0, glbnvbufsize);
char *p = glbnvbuf2;
Expand Down Expand Up @@ -730,9 +717,19 @@ static void nvimcom_globalenv_list(void) {
send_glb_env();

double tmdiff = 1000 * ((double)clock() - tm) / CLOCKS_PER_SEC;
if (verbose && tmdiff > 500.0)
REprintf("Time to build GlobalEnv cmplls [%zu bytes]: %f ms\n",
strlen(glbnvbuf2), tmdiff);
if (tmdiff > timelimit || strlen(glbnvbuf1) > sizelimit) {
maxdepth = curdepth - 1;
char b[16];
snprintf(b, 15, "+D%d", maxdepth);
send_to_nvim(b);
if (verbose)
REprintf(
"nvimcom:\n"
" Time to buiild list of objects: %g ms (max_time = %g ms)\n"
" List size: %zu bytes (max_size = %d bytes)\n"
" New max_depth: %d\n",
tmdiff, timelimit, strlen(glbnvbuf1), sizelimit, maxdepth);
}
}

/**
Expand Down Expand Up @@ -1094,6 +1091,15 @@ static void nvimcom_parse_received_msg(char *buf) {
REprintf("\nvimcom: received invalid RNVIM_ID.\n");
}
break;
case 'D':
p = buf;
p++;
maxdepth = atoi(p);
if (verbose > 3)
REprintf("New max_depth: %d\n", maxdepth);
flag_glbenv = 1;
nvimcom_fire();
break;
default: // do nothing
REprintf("\nError [nvimcom]: Invalid message received: %s\n", buf);
break;
Expand Down Expand Up @@ -1170,13 +1176,17 @@ static void *client_loop_thread(__attribute__((unused)) void *arg)
*
* @param rinfo Information on R to be passed to nvim.
*/
void nvimcom_Start(int *vrb, int *anm, int *swd, int *age, char **nvv,
char **rinfo) {
void nvimcom_Start(int *vrb, int *anm, int *swd, int *age, int *imd, int *szl,
int *tml, char **nvv, char **rinfo) {
verbose = *vrb;
allnames = *anm;
setwidth = *swd;
autoglbenv = *age;

maxdepth = *imd;
sizelimit = *szl;
timelimit = (double)*tml;

if (getenv("RNVIM_TMPDIR")) {
strncpy(tmpdir, getenv("RNVIM_TMPDIR"), 500);
if (getenv("RNVIM_SECRET"))
Expand Down

0 comments on commit dc4eb50

Please sign in to comment.