Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/extern/isocline/src/editline_completion.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
8
//-------------------------------------------------------------
9
// Completion menu: this file is included in editline.c
10
//-------------------------------------------------------------
11
12
// return true if anything changed
13
static bool edit_complete(ic_env_t* env, editor_t* eb, ssize_t idx) {
14
editor_start_modify(eb);
15
ssize_t newpos = completions_apply(env->completions, idx, eb->input, eb->pos);
16
if (newpos < 0) {
17
editor_undo_restore(eb,false);
18
return false;
19
}
20
eb->pos = newpos;
21
edit_refresh(env,eb);
22
return true;
23
}
24
25
static bool edit_complete_longest_prefix(ic_env_t* env, editor_t* eb ) {
26
editor_start_modify(eb);
27
ssize_t newpos = completions_apply_longest_prefix( env->completions, eb->input, eb->pos );
28
if (newpos < 0) {
29
editor_undo_restore(eb,false);
30
return false;
31
}
32
eb->pos = newpos;
33
edit_refresh(env,eb);
34
return true;
35
}
36
37
ic_private void sbuf_append_tagged( stringbuf_t* sb, const char* tag, const char* content ) {
38
sbuf_appendf(sb, "[%s]", tag);
39
sbuf_append(sb,content);
40
sbuf_append(sb,"[/]");
41
}
42
43
static void editor_append_completion(ic_env_t* env, editor_t* eb, ssize_t idx, ssize_t width, bool numbered, bool selected ) {
44
const char* help = NULL;
45
const char* display = completions_get_display(env->completions, idx, &help);
46
if (display == NULL) return;
47
if (numbered) {
48
sbuf_appendf(eb->extra, "[ic-info]%s%zd [/]", (selected ? (tty_is_utf8(env->tty) ? "\xE2\x86\x92" : "*") : " "), 1 + idx);
49
width -= 3;
50
}
51
52
if (width > 0) {
53
sbuf_appendf(eb->extra, "[width=\"%zd;left; ;on\"]", width );
54
}
55
if (selected) {
56
sbuf_append(eb->extra, "[ic-emphasis]");
57
}
58
sbuf_append(eb->extra, display);
59
if (selected) { sbuf_append(eb->extra,"[/ic-emphasis]"); }
60
if (help != NULL) {
61
sbuf_append(eb->extra, " ");
62
sbuf_append_tagged(eb->extra, "ic-info", help );
63
}
64
if (width > 0) { sbuf_append(eb->extra,"[/width]"); }
65
}
66
67
// 2 and 3 column output up to 80 wide
68
#define IC_DISPLAY2_MAX 34
69
#define IC_DISPLAY2_COL (3+IC_DISPLAY2_MAX)
70
#define IC_DISPLAY2_WIDTH (2*IC_DISPLAY2_COL + 2) // 75
71
72
#define IC_DISPLAY3_MAX 21
73
#define IC_DISPLAY3_COL (3+IC_DISPLAY3_MAX)
74
#define IC_DISPLAY3_WIDTH (3*IC_DISPLAY3_COL + 2*2) // 76
75
76
static void editor_append_completion2(ic_env_t* env, editor_t* eb, ssize_t col_width, ssize_t idx1, ssize_t idx2, ssize_t selected ) {
77
editor_append_completion(env, eb, idx1, col_width, true, (idx1 == selected) );
78
sbuf_append( eb->extra, " ");
79
editor_append_completion(env, eb, idx2, col_width, true, (idx2 == selected) );
80
}
81
82
static void editor_append_completion3(ic_env_t* env, editor_t* eb, ssize_t col_width, ssize_t idx1, ssize_t idx2, ssize_t idx3, ssize_t selected ) {
83
editor_append_completion(env, eb, idx1, col_width, true, (idx1 == selected) );
84
sbuf_append( eb->extra, " ");
85
editor_append_completion(env, eb, idx2, col_width, true, (idx2 == selected));
86
sbuf_append( eb->extra, " ");
87
editor_append_completion(env, eb, idx3, col_width, true, (idx3 == selected) );
88
}
89
90
static ssize_t edit_completions_max_width( ic_env_t* env, ssize_t count ) {
91
ssize_t max_width = 0;
92
for( ssize_t i = 0; i < count; i++) {
93
const char* help = NULL;
94
ssize_t w = bbcode_column_width(env->bbcode, completions_get_display(env->completions, i, &help));
95
if (help != NULL) {
96
w += 2 + bbcode_column_width(env->bbcode, help);
97
}
98
if (w > max_width) {
99
max_width = w;
100
}
101
}
102
return max_width;
103
}
104
105
static void edit_completion_menu(ic_env_t* env, editor_t* eb, bool more_available) {
106
ssize_t count = completions_count(env->completions);
107
ssize_t count_displayed = count;
108
assert(count > 1);
109
ssize_t selected = (env->complete_nopreview ? 0 : -1); // select first or none
110
ssize_t percolumn = count;
111
112
again:
113
// show first 9 (or 8) completions
114
sbuf_clear(eb->extra);
115
ssize_t twidth = term_get_width(env->term) - 1;
116
ssize_t colwidth;
117
if (count > 3 && ((colwidth = 3 + edit_completions_max_width(env, 9))*3 + 2*2) < twidth) {
118
// display as a 3 column block
119
count_displayed = (count > 9 ? 9 : count);
120
percolumn = 3;
121
for (ssize_t rw = 0; rw < percolumn; rw++) {
122
if (rw > 0) sbuf_append(eb->extra, "\n");
123
editor_append_completion3(env, eb, colwidth, rw, percolumn+rw, (2*percolumn)+rw, selected);
124
}
125
}
126
else if (count > 4 && ((colwidth = 3 + edit_completions_max_width(env, 8))*2 + 2) < twidth) {
127
// display as a 2 column block if some entries are too wide for three columns
128
count_displayed = (count > 8 ? 8 : count);
129
percolumn = (count_displayed <= 6 ? 3 : 4);
130
for (ssize_t rw = 0; rw < percolumn; rw++) {
131
if (rw > 0) sbuf_append(eb->extra, "\n");
132
editor_append_completion2(env, eb, colwidth, rw, percolumn+rw, selected);
133
}
134
}
135
else {
136
// display as a list
137
count_displayed = (count > 9 ? 9 : count);
138
percolumn = count_displayed;
139
for (ssize_t i = 0; i < count_displayed; i++) {
140
if (i > 0) sbuf_append(eb->extra, "\n");
141
editor_append_completion(env, eb, i, -1, true /* numbered */, selected == i);
142
}
143
}
144
if (count > count_displayed) {
145
if (more_available) {
146
sbuf_append(eb->extra, "\n[ic-info](press page-down (or ctrl-j) to see all further completions)[/]");
147
}
148
else {
149
sbuf_appendf(eb->extra, "\n[ic-info](press page-down (or ctrl-j) to see all %zd completions)[/]", count );
150
}
151
}
152
if (!env->complete_nopreview && selected >= 0 && selected <= count_displayed) {
153
edit_complete(env,eb,selected);
154
editor_undo_restore(eb,false);
155
}
156
else {
157
edit_refresh(env, eb);
158
}
159
160
// read here; if not a valid key, push it back and return to main event loop
161
code_t c = tty_read(env->tty);
162
if (tty_term_resize_event(env->tty)) {
163
edit_resize(env, eb);
164
}
165
sbuf_clear(eb->extra);
166
167
// direct selection?
168
if (c >= '1' && c <= '9') {
169
ssize_t i = (c - '1');
170
if (i < count) {
171
selected = i;
172
c = KEY_ENTER;
173
}
174
}
175
176
// process commands
177
if (c == KEY_DOWN || c == KEY_TAB) {
178
selected++;
179
if (selected >= count_displayed) {
180
//term_beep(env->term);
181
selected = 0;
182
}
183
goto again;
184
}
185
else if (c == KEY_UP || c == KEY_SHIFT_TAB) {
186
selected--;
187
if (selected < 0) {
188
selected = count_displayed - 1;
189
//term_beep(env->term);
190
}
191
goto again;
192
}
193
else if (c == KEY_F1) {
194
edit_show_help(env, eb);
195
goto again;
196
}
197
else if (c == KEY_ESC) {
198
completions_clear(env->completions);
199
edit_refresh(env,eb);
200
c = 0; // ignore and return
201
}
202
else if (selected >= 0 && (c == KEY_ENTER || c == KEY_RIGHT || c == KEY_END)) /* || c == KEY_TAB*/ {
203
// select the current entry
204
assert(selected < count);
205
c = 0;
206
edit_complete(env, eb, selected);
207
if (env->complete_autotab) {
208
tty_code_pushback(env->tty,KEY_EVENT_AUTOTAB); // immediately try to complete again
209
}
210
}
211
else if (!env->complete_nopreview && !code_is_virt_key(c)) {
212
// if in preview mode, select the current entry and exit the menu
213
assert(selected < count);
214
edit_complete(env, eb, selected);
215
}
216
else if ((c == KEY_PAGEDOWN || c == KEY_LINEFEED) && count > 9) {
217
// show all completions
218
c = 0;
219
if (more_available) {
220
// generate all entries (up to the max (= 1000))
221
count = completions_generate(env, env->completions, sbuf_string(eb->input), eb->pos, IC_MAX_COMPLETIONS_TO_SHOW);
222
}
223
rowcol_t rc;
224
edit_get_rowcol(env,eb,&rc);
225
edit_clear(env,eb);
226
edit_write_prompt(env,eb,0,false);
227
term_writeln(env->term, "");
228
for(ssize_t i = 0; i < count; i++) {
229
const char* display = completions_get_display(env->completions, i, NULL);
230
if (display != NULL) {
231
bbcode_println(env->bbcode, display);
232
}
233
}
234
if (count >= IC_MAX_COMPLETIONS_TO_SHOW) {
235
bbcode_println(env->bbcode, "[ic-info]... and more.[/]");
236
}
237
else {
238
bbcode_printf(env->bbcode, "[ic-info](%zd possible completions)[/]\n", count );
239
}
240
for(ssize_t i = 0; i < rc.row+1; i++) {
241
term_write(env->term, " \n");
242
}
243
eb->cur_rows = 0;
244
edit_refresh(env,eb);
245
}
246
else {
247
edit_refresh(env,eb);
248
}
249
// done
250
completions_clear(env->completions);
251
if (c != 0) tty_code_pushback(env->tty,c);
252
}
253
254
static void edit_generate_completions(ic_env_t* env, editor_t* eb, bool autotab) {
255
debug_msg( "edit: complete: %zd: %s\n", eb->pos, sbuf_string(eb->input) );
256
if (eb->pos < 0) return;
257
ssize_t count = completions_generate(env, env->completions, sbuf_string(eb->input), eb->pos, IC_MAX_COMPLETIONS_TO_TRY);
258
bool more_available = (count >= IC_MAX_COMPLETIONS_TO_TRY);
259
if (count <= 0) {
260
// no completions
261
if (!autotab) { term_beep(env->term); }
262
}
263
else if (count == 1) {
264
// complete if only one match
265
if (edit_complete(env,eb,0 /*idx*/) && env->complete_autotab) {
266
tty_code_pushback(env->tty,KEY_EVENT_AUTOTAB);
267
}
268
}
269
else {
270
//term_beep(env->term);
271
if (!more_available) {
272
edit_complete_longest_prefix(env,eb);
273
}
274
completions_sort(env->completions);
275
edit_completion_menu( env, eb, more_available);
276
}
277
}
278
279