1 /*
2 * R : A Computer Language for Statistical Data Analysis
3 * file rt_complete.c
4 * Copyright (C) 2007-2017 The R Core Team.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, a copy is available at
18 * https://www.R-project.org/Licenses/
19 */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <getline/getline.h>
27 #include <string.h>
28 #include <stdlib.h> /* for getenv */
29
30 #ifndef min
31 /* in stdlib.h in Win64 headers */
32 # define min(a, b) (a < b ? a : b)
33 #endif
34
35 #include <Rinternals.h>
36 #include <R_ext/Parse.h>
37
38 static int completion_available = -1;
39
gl_tab(char * buf,int offset,int * loc)40 static int gl_tab(char *buf, int offset, int *loc)
41 /* default tab handler, acts like tabstops every 8 cols */
42 {
43 int i, count, len;
44
45 len = strlen(buf);
46 count = 8 - (offset + *loc) % 8;
47 for (i=len; i >= *loc; i--)
48 buf[i+count] = buf[i];
49 for (i=0; i < count; i++)
50 buf[*loc+i] = ' ';
51 i = *loc;
52 *loc = i + count;
53 return i;
54 }
55
rt_completion(char * buf,int offset,int * loc)56 static int rt_completion(char *buf, int offset, int *loc)
57 {
58 int i, alen, cursor_position = *loc;
59 char *partial_line = buf;
60 const char *additional_text;
61 SEXP cmdSexp, cmdexpr, ans = R_NilValue;
62 ParseStatus status;
63
64 if(!completion_available) return gl_tab(buf, offset, loc);
65
66 if(completion_available < 0) {
67 char *p = getenv("R_COMPLETION");
68 if(p && strcmp(p, "FALSE") == 0) {
69 completion_available = 0;
70 return gl_tab(buf, offset, loc);
71 }
72 /* First check if namespace is loaded */
73 if(findVarInFrame(R_NamespaceRegistry, install("utils"))
74 != R_UnboundValue) completion_available = 1;
75 else { /* Then try to load it */
76 char *p = "try(loadNamespace('utils'), silent=TRUE)";
77 PROTECT(cmdSexp = mkString(p));
78 cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
79 if(status == PARSE_OK) {
80 for(i = 0; i < length(cmdexpr); i++)
81 eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
82 }
83 UNPROTECT(2);
84 if(findVarInFrame(R_NamespaceRegistry, install("utils"))
85 != R_UnboundValue) completion_available = 1;
86 else {
87 completion_available = 0;
88 return -1; /* no change */
89 }
90 }
91 }
92
93 alen = strlen(partial_line);
94 char orig[alen + 1], pline[2*alen + 1],
95 *pchar = pline, achar;
96 strcpy(orig, partial_line);
97 for (i = 0; i < alen; i++) {
98 achar = orig[i];
99 if (achar == '"' || achar == '\\') *pchar++ = '\\';
100 *pchar++ = achar;
101 }
102 *pchar = 0;
103 size_t len = strlen(pline) + 100;
104 char cmd[len];
105 snprintf(cmd, len,
106 "utils:::.win32consoleCompletion(\"%s\", %d)",
107 pline, cursor_position);
108 PROTECT(cmdSexp = mkString(cmd));
109 cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
110 if (status != PARSE_OK) {
111 UNPROTECT(2);
112 /* Uncomment next line to debug */
113 /* Rprintf("failed: %s \n", cmd); */
114 /* otherwise pretend that nothing happened and return */
115 return -1; /* no change */
116 }
117 /* Loop is needed here as EXPRSEXP will be of length > 1 */
118 for(i = 0; i < length(cmdexpr); i++)
119 ans = eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
120 UNPROTECT(2);
121
122 /* ans has the form list(addition, possible), where 'addition' is
123 unique additional text if any, and 'possible' is a character
124 vector holding possible completions if any (already formatted
125 for linewise printing in the current implementation). If
126 'possible' has any content, we want to print those (or show in
127 status bar or whatever). Otherwise add the 'additional' text
128 at the cursor */
129
130 #define ADDITION 0
131 #define POSSIBLE 1
132
133 alen = length(VECTOR_ELT(ans, POSSIBLE));
134 if (alen) {
135 int max_show = 10;
136 printf("\n"); /* finish current line */
137 for (i = 0; i < min(alen, max_show); i++) {
138 printf("%s\n", CHAR(STRING_ELT(VECTOR_ELT(ans, POSSIBLE), i)));
139 }
140 if (alen > max_show)
141 printf("\n[...truncated]\n");
142 cursor_position = -2; /* Need to redisplay whole line */
143 }
144 additional_text = CHAR(STRING_ELT( VECTOR_ELT(ans, ADDITION), 0 ));
145 alen = strlen(additional_text);
146 if (alen) {
147 int cp = *loc;
148 memcpy(buf+cp, additional_text, alen+1);
149 *loc = cp + alen;
150 }
151 return cursor_position;
152 }
153
154
R_gl_tab_set(void)155 void R_gl_tab_set(void)
156 {
157 gl_tab_hook = rt_completion;
158 }
159