Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/extern/isocline/src/completions.c
2727 views
1
/* ----------------------------------------------------------------------------
2
Copyright (c) 2021, Daan Leijen
3
This is free software; you can redistribute it and/or modify it
4
under the terms of the MIT License. A copy of the license can be
5
found in the "LICENSE" file at the root of this distribution.
6
-----------------------------------------------------------------------------*/
7
#include <string.h>
8
#include <stdio.h>
9
#include <stdlib.h>
10
11
#include "../include/isocline.h"
12
#include "common.h"
13
#include "env.h"
14
#include "stringbuf.h"
15
#include "completions.h"
16
17
18
//-------------------------------------------------------------
19
// Completions
20
//-------------------------------------------------------------
21
22
typedef struct completion_s {
23
const char* replacement;
24
const char* display;
25
const char* help;
26
ssize_t delete_before;
27
ssize_t delete_after;
28
} completion_t;
29
30
struct completions_s {
31
ic_completer_fun_t* completer;
32
void* completer_arg;
33
ssize_t completer_max;
34
ssize_t count;
35
ssize_t len;
36
completion_t* elems;
37
alloc_t* mem;
38
};
39
40
static void default_filename_completer( ic_completion_env_t* cenv, const char* prefix );
41
42
ic_private completions_t* completions_new(alloc_t* mem) {
43
completions_t* cms = mem_zalloc_tp(mem, completions_t);
44
if (cms == NULL) return NULL;
45
cms->mem = mem;
46
cms->completer = &default_filename_completer;
47
return cms;
48
}
49
50
ic_private void completions_free(completions_t* cms) {
51
if (cms == NULL) return;
52
completions_clear(cms);
53
if (cms->elems != NULL) {
54
mem_free(cms->mem, cms->elems);
55
cms->elems = NULL;
56
cms->count = 0;
57
cms->len = 0;
58
}
59
mem_free(cms->mem, cms); // free ourselves
60
}
61
62
63
ic_private void completions_clear(completions_t* cms) {
64
while (cms->count > 0) {
65
completion_t* cm = cms->elems + cms->count - 1;
66
mem_free( cms->mem, cm->display);
67
mem_free( cms->mem, cm->replacement);
68
mem_free( cms->mem, cm->help);
69
memset(cm,0,sizeof(*cm));
70
cms->count--;
71
}
72
}
73
74
static void completions_push(completions_t* cms, const char* replacement, const char* display, const char* help, ssize_t delete_before, ssize_t delete_after)
75
{
76
if (cms->count >= cms->len) {
77
ssize_t newlen = (cms->len <= 0 ? 32 : cms->len*2);
78
completion_t* newelems = mem_realloc_tp(cms->mem, completion_t, cms->elems, newlen );
79
if (newelems == NULL) return;
80
cms->elems = newelems;
81
cms->len = newlen;
82
}
83
assert(cms->count < cms->len);
84
completion_t* cm = cms->elems + cms->count;
85
cm->replacement = mem_strdup(cms->mem,replacement);
86
cm->display = mem_strdup(cms->mem,display);
87
cm->help = mem_strdup(cms->mem,help);
88
cm->delete_before = delete_before;
89
cm->delete_after = delete_after;
90
cms->count++;
91
}
92
93
ic_private ssize_t completions_count(completions_t* cms) {
94
return cms->count;
95
}
96
97
static bool completions_contains(completions_t* cms, const char* replacement) {
98
for( ssize_t i = 0; i < cms->count; i++ ) {
99
const completion_t* c = cms->elems + i;
100
if (strcmp(replacement,c->replacement) == 0) { return true; }
101
}
102
return false;
103
}
104
105
ic_private bool completions_add(completions_t* cms, const char* replacement, const char* display, const char* help, ssize_t delete_before, ssize_t delete_after) {
106
if (cms->completer_max <= 0) return false;
107
cms->completer_max--;
108
//debug_msg("completion: add: %d,%d, %s\n", delete_before, delete_after, replacement);
109
if (!completions_contains(cms,replacement)) {
110
completions_push(cms, replacement, display, help, delete_before, delete_after);
111
}
112
return true;
113
}
114
115
static completion_t* completions_get(completions_t* cms, ssize_t index) {
116
if (index < 0 || cms->count <= 0 || index >= cms->count) return NULL;
117
return &cms->elems[index];
118
}
119
120
ic_private const char* completions_get_display( completions_t* cms, ssize_t index, const char** help ) {
121
if (help != NULL) { *help = NULL; }
122
completion_t* cm = completions_get(cms, index);
123
if (cm == NULL) return NULL;
124
if (help != NULL) { *help = cm->help; }
125
return (cm->display != NULL ? cm->display : cm->replacement);
126
}
127
128
ic_private const char* completions_get_help( completions_t* cms, ssize_t index ) {
129
completion_t* cm = completions_get(cms, index);
130
if (cm == NULL) return NULL;
131
return cm->help;
132
}
133
134
ic_private const char* completions_get_hint(completions_t* cms, ssize_t index, const char** help) {
135
if (help != NULL) { *help = NULL; }
136
completion_t* cm = completions_get(cms, index);
137
if (cm == NULL) return NULL;
138
ssize_t len = ic_strlen(cm->replacement);
139
if (len < cm->delete_before) return NULL;
140
const char* hint = (cm->replacement + cm->delete_before);
141
if (*hint == 0 || utf8_is_cont((uint8_t)(*hint))) return NULL; // utf8 boundary?
142
if (help != NULL) { *help = cm->help; }
143
return hint;
144
}
145
146
ic_private void completions_set_completer(completions_t* cms, ic_completer_fun_t* completer, void* arg) {
147
cms->completer = completer;
148
cms->completer_arg = arg;
149
}
150
151
ic_private void completions_get_completer(completions_t* cms, ic_completer_fun_t** completer, void** arg) {
152
*completer = cms->completer;
153
*arg = cms->completer_arg;
154
}
155
156
157
ic_public void* ic_completion_arg( const ic_completion_env_t* cenv ) {
158
return (cenv == NULL ? NULL : cenv->env->completions->completer_arg);
159
}
160
161
ic_public bool ic_has_completions( const ic_completion_env_t* cenv ) {
162
return (cenv == NULL ? false : cenv->env->completions->count > 0);
163
}
164
165
ic_public bool ic_stop_completing( const ic_completion_env_t* cenv) {
166
return (cenv == NULL ? true : cenv->env->completions->completer_max <= 0);
167
}
168
169
170
static ssize_t completion_apply( completion_t* cm, stringbuf_t* sbuf, ssize_t pos ) {
171
if (cm == NULL) return -1;
172
debug_msg( "completion: apply: %s at %zd\n", cm->replacement, pos);
173
ssize_t start = pos - cm->delete_before;
174
if (start < 0) start = 0;
175
ssize_t n = cm->delete_before + cm->delete_after;
176
if (ic_strlen(cm->replacement) == n && strncmp(sbuf_string_at(sbuf,start), cm->replacement, to_size_t(n)) == 0) {
177
// no changes
178
return -1;
179
}
180
else {
181
sbuf_delete_from_to( sbuf, start, pos + cm->delete_after );
182
return sbuf_insert_at(sbuf, cm->replacement, start);
183
}
184
}
185
186
ic_private ssize_t completions_apply( completions_t* cms, ssize_t index, stringbuf_t* sbuf, ssize_t pos ) {
187
completion_t* cm = completions_get(cms, index);
188
return completion_apply( cm, sbuf, pos );
189
}
190
191
192
static int completion_compare(const void* p1, const void* p2) {
193
if (p1 == NULL || p2 == NULL) return 0;
194
const completion_t* cm1 = (const completion_t*)p1;
195
const completion_t* cm2 = (const completion_t*)p2;
196
return ic_stricmp(cm1->replacement, cm2->replacement);
197
}
198
199
ic_private void completions_sort(completions_t* cms) {
200
if (cms->count <= 0) return;
201
qsort(cms->elems, to_size_t(cms->count), sizeof(cms->elems[0]), &completion_compare);
202
}
203
204
#define IC_MAX_PREFIX (256)
205
206
// find longest common prefix and complete with that.
207
ic_private ssize_t completions_apply_longest_prefix(completions_t* cms, stringbuf_t* sbuf, ssize_t pos) {
208
if (cms->count <= 1) {
209
return completions_apply(cms,0,sbuf,pos);
210
}
211
212
// set initial prefix to the first entry
213
completion_t* cm = completions_get(cms, 0);
214
if (cm == NULL) return -1;
215
216
char prefix[IC_MAX_PREFIX+1];
217
ssize_t delete_before = cm->delete_before;
218
ic_strncpy( prefix, IC_MAX_PREFIX+1, cm->replacement, IC_MAX_PREFIX );
219
prefix[IC_MAX_PREFIX] = 0;
220
221
// and visit all others to find the longest common prefix
222
for(ssize_t i = 1; i < cms->count; i++) {
223
cm = completions_get(cms,i);
224
if (cm->delete_before != delete_before) { // deletions must match delete_before
225
prefix[0] = 0;
226
break;
227
}
228
// check if it is still a prefix
229
const char* r = cm->replacement;
230
ssize_t j;
231
for(j = 0; prefix[j] != 0 && r[j] != 0; j++) {
232
if (prefix[j] != r[j]) break;
233
}
234
prefix[j] = 0;
235
if (j <= 0) break;
236
}
237
238
// check the length
239
ssize_t len = ic_strlen(prefix);
240
if (len <= 0 || len < delete_before) return -1;
241
242
// we found a prefix :-)
243
completion_t cprefix;
244
memset(&cprefix,0,sizeof(cprefix));
245
cprefix.delete_before = delete_before;
246
cprefix.replacement = prefix;
247
ssize_t newpos = completion_apply( &cprefix, sbuf, pos);
248
if (newpos < 0) return newpos;
249
250
// adjust all delete_before for the new replacement
251
for( ssize_t i = 0; i < cms->count; i++) {
252
cm = completions_get(cms,i);
253
cm->delete_before = len;
254
}
255
256
return newpos;
257
}
258
259
260
//-------------------------------------------------------------
261
// Completer functions
262
//-------------------------------------------------------------
263
264
ic_public bool ic_add_completions(ic_completion_env_t* cenv, const char* prefix, const char** completions) {
265
for (const char** pc = completions; *pc != NULL; pc++) {
266
if (ic_istarts_with(*pc, prefix)) {
267
if (!ic_add_completion_ex(cenv, *pc, NULL, NULL)) return false;
268
}
269
}
270
return true;
271
}
272
273
ic_public bool ic_add_completion(ic_completion_env_t* cenv, const char* replacement) {
274
return ic_add_completion_ex(cenv, replacement, NULL, NULL);
275
}
276
277
ic_public bool ic_add_completion_ex( ic_completion_env_t* cenv, const char* replacement, const char* display, const char* help ) {
278
return ic_add_completion_prim(cenv,replacement,display,help,0,0);
279
}
280
281
ic_public bool ic_add_completion_prim(ic_completion_env_t* cenv, const char* replacement, const char* display, const char* help, long delete_before, long delete_after) {
282
return (*cenv->complete)(cenv->env, cenv->closure, replacement, display, help, delete_before, delete_after );
283
}
284
285
static bool prim_add_completion(ic_env_t* env, void* funenv, const char* replacement, const char* display, const char* help, long delete_before, long delete_after) {
286
ic_unused(funenv);
287
return completions_add(env->completions, replacement, display, help, delete_before, delete_after);
288
}
289
290
ic_public void ic_set_default_completer(ic_completer_fun_t* completer, void* arg) {
291
ic_env_t* env = ic_get_env(); if (env == NULL) return;
292
completions_set_completer(env->completions, completer, arg);
293
}
294
295
ic_private ssize_t completions_generate(struct ic_env_s* env, completions_t* cms, const char* input, ssize_t pos, ssize_t max) {
296
completions_clear(cms);
297
if (cms->completer == NULL || input == NULL || ic_strlen(input) < pos) return 0;
298
299
// set up env
300
ic_completion_env_t cenv;
301
cenv.env = env;
302
cenv.input = input,
303
cenv.cursor = (long)pos;
304
cenv.arg = cms->completer_arg;
305
cenv.complete = &prim_add_completion;
306
cenv.closure = NULL;
307
const char* prefix = mem_strndup(cms->mem, input, pos);
308
cms->completer_max = max;
309
310
// and complete
311
cms->completer(&cenv,prefix);
312
313
// restore
314
mem_free(cms->mem,prefix);
315
return completions_count(cms);
316
}
317
318
// The default completer is no completion is set
319
static void default_filename_completer( ic_completion_env_t* cenv, const char* prefix ) {
320
#ifdef _WIN32
321
const char sep = '\\';
322
#else
323
const char sep = '/';
324
#endif
325
ic_complete_filename( cenv, prefix, sep, ".", NULL);
326
}
327
328