Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/extern/isocline/src/editline_history.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
// History search: this file is included in editline.c
10
//-------------------------------------------------------------
11
12
static void edit_history_at(ic_env_t* env, editor_t* eb, int ofs )
13
{
14
if (eb->modified) {
15
history_update(env->history, sbuf_string(eb->input)); // update first entry if modified
16
eb->history_idx = 0; // and start again
17
eb->modified = false;
18
}
19
const char* entry = history_get(env->history,eb->history_idx + ofs);
20
// debug_msg( "edit: history: at: %d + %d, found: %s\n", eb->history_idx, ofs, entry);
21
if (entry == NULL) {
22
term_beep(env->term);
23
}
24
else {
25
eb->history_idx += ofs;
26
sbuf_replace(eb->input, entry);
27
if (ofs > 0) {
28
// at end of first line when scrolling up
29
ssize_t end = sbuf_find_line_end(eb->input,0);
30
eb->pos = (end < 0 ? 0 : end);
31
}
32
else {
33
eb->pos = sbuf_len(eb->input); // at end of last line when scrolling down
34
}
35
edit_refresh(env, eb);
36
}
37
}
38
39
static void edit_history_prev(ic_env_t* env, editor_t* eb) {
40
edit_history_at(env,eb, 1 );
41
}
42
43
static void edit_history_next(ic_env_t* env, editor_t* eb) {
44
edit_history_at(env,eb, -1 );
45
}
46
47
typedef struct hsearch_s {
48
struct hsearch_s* next;
49
ssize_t hidx;
50
ssize_t match_pos;
51
ssize_t match_len;
52
bool cinsert;
53
} hsearch_t;
54
55
static void hsearch_push( alloc_t* mem, hsearch_t** hs, ssize_t hidx, ssize_t mpos, ssize_t mlen, bool cinsert ) {
56
hsearch_t* h = mem_zalloc_tp( mem, hsearch_t );
57
if (h == NULL) return;
58
h->hidx = hidx;
59
h->match_pos = mpos;
60
h->match_len = mlen;
61
h->cinsert = cinsert;
62
h->next = *hs;
63
*hs = h;
64
}
65
66
static bool hsearch_pop( alloc_t* mem, hsearch_t** hs, ssize_t* hidx, ssize_t* match_pos, ssize_t* match_len, bool* cinsert ) {
67
hsearch_t* h = *hs;
68
if (h == NULL) return false;
69
*hs = h->next;
70
if (hidx != NULL) *hidx = h->hidx;
71
if (match_pos != NULL) *match_pos = h->match_pos;
72
if (match_len != NULL) *match_len = h->match_len;
73
if (cinsert != NULL) *cinsert = h->cinsert;
74
mem_free(mem, h);
75
return true;
76
}
77
78
static void hsearch_done( alloc_t* mem, hsearch_t* hs ) {
79
while (hs != NULL) {
80
hsearch_t* next = hs->next;
81
mem_free(mem, hs);
82
hs = next;
83
}
84
}
85
86
static void edit_history_search(ic_env_t* env, editor_t* eb, char* initial ) {
87
if (history_count( env->history ) <= 0) {
88
term_beep(env->term);
89
return;
90
}
91
92
// update history
93
if (eb->modified) {
94
history_update(env->history, sbuf_string(eb->input)); // update first entry if modified
95
eb->history_idx = 0; // and start again
96
eb->modified = false;
97
}
98
99
// set a search prompt and remember the previous state
100
editor_undo_capture(eb);
101
eb->disable_undo = true;
102
bool old_hint = ic_enable_hint(false);
103
const char* prompt_text = eb->prompt_text;
104
eb->prompt_text = "history search";
105
106
// search state
107
hsearch_t* hs = NULL; // search undo
108
ssize_t hidx = 1; // current history entry
109
ssize_t match_pos = 0; // current matched position
110
ssize_t match_len = 0; // length of the match
111
const char* hentry = NULL; // current history entry
112
113
// Simulate per character searches for each letter in `initial` (so backspace works)
114
if (initial != NULL) {
115
const ssize_t initial_len = ic_strlen(initial);
116
ssize_t ipos = 0;
117
while( ipos < initial_len ) {
118
ssize_t next = str_next_ofs( initial, initial_len, ipos, NULL );
119
if (next < 0) break;
120
hsearch_push( eb->mem, &hs, hidx, match_pos, match_len, true);
121
char c = initial[ipos + next]; // terminate temporarily
122
initial[ipos + next] = 0;
123
if (history_search( env->history, hidx, initial, true, &hidx, &match_pos )) {
124
match_len = ipos + next;
125
}
126
else if (ipos + next >= initial_len) {
127
term_beep(env->term);
128
}
129
initial[ipos + next] = c; // restore
130
ipos += next;
131
}
132
sbuf_replace( eb->input, initial);
133
eb->pos = ipos;
134
}
135
else {
136
sbuf_clear( eb->input );
137
eb->pos = 0;
138
}
139
140
// Incremental search
141
again:
142
hentry = history_get(env->history,hidx);
143
if (hentry != NULL) {
144
sbuf_appendf(eb->extra, "[ic-info]%zd. [/][ic-diminish][!pre]", hidx);
145
sbuf_append_n( eb->extra, hentry, match_pos );
146
sbuf_append(eb->extra, "[/pre][u ic-emphasis][!pre]" );
147
sbuf_append_n( eb->extra, hentry + match_pos, match_len );
148
sbuf_append(eb->extra, "[/pre][/u][!pre]" );
149
sbuf_append(eb->extra, hentry + match_pos + match_len );
150
sbuf_append(eb->extra, "[/pre][/ic-diminish]");
151
if (!env->no_help) {
152
sbuf_append(eb->extra, "\n[ic-info](use tab for the next match)[/]");
153
}
154
sbuf_append(eb->extra, "\n" );
155
}
156
edit_refresh(env, eb);
157
158
// Wait for input
159
code_t c = (hentry == NULL ? KEY_ESC : tty_read(env->tty));
160
if (tty_term_resize_event(env->tty)) {
161
edit_resize(env, eb);
162
}
163
sbuf_clear(eb->extra);
164
165
// Process commands
166
if (c == KEY_ESC || c == KEY_BELL /* ^G */ || c == KEY_CTRL_C) {
167
c = 0;
168
eb->disable_undo = false;
169
editor_undo_restore(eb, false);
170
}
171
else if (c == KEY_ENTER) {
172
c = 0;
173
editor_undo_forget(eb);
174
sbuf_replace( eb->input, hentry );
175
eb->pos = sbuf_len(eb->input);
176
eb->modified = false;
177
eb->history_idx = hidx;
178
}
179
else if (c == KEY_BACKSP || c == KEY_CTRL_Z) {
180
// undo last search action
181
bool cinsert;
182
if (hsearch_pop(env->mem,&hs, &hidx, &match_pos, &match_len, &cinsert)) {
183
if (cinsert) edit_backspace(env,eb);
184
}
185
goto again;
186
}
187
else if (c == KEY_CTRL_R || c == KEY_TAB || c == KEY_UP) {
188
// search backward
189
hsearch_push(env->mem, &hs, hidx, match_pos, match_len, false);
190
if (!history_search( env->history, hidx+1, sbuf_string(eb->input), true, &hidx, &match_pos )) {
191
hsearch_pop(env->mem,&hs,NULL,NULL,NULL,NULL);
192
term_beep(env->term);
193
};
194
goto again;
195
}
196
else if (c == KEY_CTRL_S || c == KEY_SHIFT_TAB || c == KEY_DOWN) {
197
// search forward
198
hsearch_push(env->mem, &hs, hidx, match_pos, match_len, false);
199
if (!history_search( env->history, hidx-1, sbuf_string(eb->input), false, &hidx, &match_pos )) {
200
hsearch_pop(env->mem, &hs,NULL,NULL,NULL,NULL);
201
term_beep(env->term);
202
};
203
goto again;
204
}
205
else if (c == KEY_F1) {
206
edit_show_help(env, eb);
207
goto again;
208
}
209
else {
210
// insert character and search further backward
211
char chr;
212
unicode_t uchr;
213
if (code_is_ascii_char(c,&chr)) {
214
hsearch_push(env->mem, &hs, hidx, match_pos, match_len, true);
215
edit_insert_char(env,eb,chr);
216
}
217
else if (code_is_unicode(c,&uchr)) {
218
hsearch_push(env->mem, &hs, hidx, match_pos, match_len, true);
219
edit_insert_unicode(env,eb,uchr);
220
}
221
else {
222
// ignore command
223
term_beep(env->term);
224
goto again;
225
}
226
// search for the new input
227
if (history_search( env->history, hidx, sbuf_string(eb->input), true, &hidx, &match_pos )) {
228
match_len = sbuf_len(eb->input);
229
}
230
else {
231
term_beep(env->term);
232
};
233
goto again;
234
}
235
236
// done
237
eb->disable_undo = false;
238
hsearch_done(env->mem,hs);
239
eb->prompt_text = prompt_text;
240
ic_enable_hint(old_hint);
241
edit_refresh(env,eb);
242
if (c != 0) tty_code_pushback(env->tty, c);
243
}
244
245
// Start an incremental search with the current word
246
static void edit_history_search_with_current_word(ic_env_t* env, editor_t* eb) {
247
char* initial = NULL;
248
ssize_t start = sbuf_find_word_start( eb->input, eb->pos );
249
if (start >= 0) {
250
const ssize_t next = sbuf_next(eb->input, start, NULL);
251
if (!ic_char_is_idletter(sbuf_string(eb->input) + start, (long)(next - start))) {
252
start = next;
253
}
254
if (start >= 0 && start < eb->pos) {
255
initial = mem_strndup(eb->mem, sbuf_string(eb->input) + start, eb->pos - start);
256
}
257
}
258
edit_history_search( env, eb, initial);
259
mem_free(env->mem, initial);
260
}
261
262