Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/extern/isocline/src/isocline.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
// Usually we include all sources one file so no internal
10
// symbols are public in the libray.
11
//
12
// You can compile the entire library just as:
13
// $ gcc -c src/isocline.c
14
//-------------------------------------------------------------
15
#if !defined(IC_SEPARATE_OBJS)
16
# ifndef _CRT_NONSTDC_NO_WARNINGS
17
# define _CRT_NONSTDC_NO_WARNINGS // for msvc
18
# endif
19
# ifndef _CRT_SECURE_NO_WARNINGS
20
# define _CRT_SECURE_NO_WARNINGS // for msvc
21
# endif
22
# define _XOPEN_SOURCE 700 // for wcwidth
23
# define _DEFAULT_SOURCE // ensure usleep stays visible with _XOPEN_SOURCE >= 700
24
# include "attr.c"
25
# include "bbcode.c"
26
# include "editline.c"
27
# include "highlight.c"
28
# include "undo.c"
29
# include "history.c"
30
# include "completers.c"
31
# include "completions.c"
32
# include "term.c"
33
# include "tty_esc.c"
34
# include "tty.c"
35
# include "stringbuf.c"
36
# include "common.c"
37
#endif
38
39
//-------------------------------------------------------------
40
// includes
41
//-------------------------------------------------------------
42
#include <stdlib.h>
43
#include <stdio.h>
44
#include <string.h>
45
#include <stdarg.h>
46
47
#include "../include/isocline.h"
48
#include "common.h"
49
#include "env.h"
50
51
52
//-------------------------------------------------------------
53
// Readline
54
//-------------------------------------------------------------
55
56
static char* ic_getline( alloc_t* mem );
57
58
ic_public char* ic_readline(const char* prompt_text)
59
{
60
ic_env_t* env = ic_get_env();
61
if (env == NULL) return NULL;
62
if (!env->noedit) {
63
// terminal editing enabled
64
return ic_editline(env, prompt_text); // in editline.c
65
}
66
else {
67
// no editing capability (pipe, dumb terminal, etc)
68
if (env->tty != NULL && env->term != NULL) {
69
// if the terminal is not interactive, but we are reading from the tty (keyboard), we display a prompt
70
term_start_raw(env->term); // set utf8 mode on windows
71
if (prompt_text != NULL) {
72
term_write(env->term, prompt_text);
73
}
74
term_write(env->term, env->prompt_marker);
75
term_end_raw(env->term, false);
76
}
77
// read directly from stdin
78
return ic_getline(env->mem);
79
}
80
}
81
82
83
//-------------------------------------------------------------
84
// Read a line from the stdin stream if there is no editing
85
// support (like from a pipe, file, or dumb terminal).
86
//-------------------------------------------------------------
87
88
static char* ic_getline(alloc_t* mem)
89
{
90
// read until eof or newline
91
stringbuf_t* sb = sbuf_new(mem);
92
int c;
93
while (true) {
94
c = fgetc(stdin);
95
if (c==EOF || c=='\n') {
96
break;
97
}
98
else {
99
sbuf_append_char(sb, (char)c);
100
}
101
}
102
return sbuf_free_dup(sb);
103
}
104
105
106
//-------------------------------------------------------------
107
// Formatted output
108
//-------------------------------------------------------------
109
110
111
ic_public void ic_printf(const char* fmt, ...) {
112
va_list ap;
113
va_start(ap, fmt);
114
ic_vprintf(fmt, ap);
115
va_end(ap);
116
}
117
118
ic_public void ic_vprintf(const char* fmt, va_list args) {
119
ic_env_t* env = ic_get_env(); if (env==NULL || env->bbcode == NULL) return;
120
bbcode_vprintf(env->bbcode, fmt, args);
121
}
122
123
ic_public void ic_print(const char* s) {
124
ic_env_t* env = ic_get_env(); if (env==NULL || env->bbcode==NULL) return;
125
bbcode_print(env->bbcode, s);
126
}
127
128
ic_public void ic_println(const char* s) {
129
ic_env_t* env = ic_get_env(); if (env==NULL || env->bbcode==NULL) return;
130
bbcode_println(env->bbcode, s);
131
}
132
133
void ic_style_def(const char* name, const char* fmt) {
134
ic_env_t* env = ic_get_env(); if (env==NULL || env->bbcode==NULL) return;
135
bbcode_style_def(env->bbcode, name, fmt);
136
}
137
138
void ic_style_open(const char* fmt) {
139
ic_env_t* env = ic_get_env(); if (env==NULL || env->bbcode==NULL) return;
140
bbcode_style_open(env->bbcode, fmt);
141
}
142
143
void ic_style_close(void) {
144
ic_env_t* env = ic_get_env(); if (env==NULL || env->bbcode==NULL) return;
145
bbcode_style_close(env->bbcode, NULL);
146
}
147
148
149
//-------------------------------------------------------------
150
// Interface
151
//-------------------------------------------------------------
152
153
ic_public bool ic_async_stop(void) {
154
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
155
if (env->tty==NULL) return false;
156
return tty_async_stop(env->tty);
157
}
158
159
static void set_prompt_marker(ic_env_t* env, const char* prompt_marker, const char* cprompt_marker) {
160
if (prompt_marker == NULL) prompt_marker = "> ";
161
if (cprompt_marker == NULL) cprompt_marker = prompt_marker;
162
mem_free(env->mem, env->prompt_marker);
163
mem_free(env->mem, env->cprompt_marker);
164
env->prompt_marker = mem_strdup(env->mem, prompt_marker);
165
env->cprompt_marker = mem_strdup(env->mem, cprompt_marker);
166
}
167
168
ic_public const char* ic_get_prompt_marker(void) {
169
ic_env_t* env = ic_get_env(); if (env==NULL) return NULL;
170
return env->prompt_marker;
171
}
172
173
ic_public const char* ic_get_continuation_prompt_marker(void) {
174
ic_env_t* env = ic_get_env(); if (env==NULL) return NULL;
175
return env->cprompt_marker;
176
}
177
178
ic_public void ic_set_prompt_marker( const char* prompt_marker, const char* cprompt_marker ) {
179
ic_env_t* env = ic_get_env(); if (env==NULL) return;
180
set_prompt_marker(env, prompt_marker, cprompt_marker);
181
}
182
183
ic_public bool ic_enable_multiline( bool enable ) {
184
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
185
bool prev = env->singleline_only;
186
env->singleline_only = !enable;
187
return !prev;
188
}
189
190
ic_public bool ic_enable_beep( bool enable ) {
191
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
192
return term_enable_beep(env->term, enable);
193
}
194
195
ic_public bool ic_enable_color( bool enable ) {
196
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
197
return term_enable_color( env->term, enable );
198
}
199
200
ic_public bool ic_enable_history_duplicates( bool enable ) {
201
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
202
return history_enable_duplicates(env->history, enable);
203
}
204
205
ic_public void ic_set_history(const char* fname, long max_entries ) {
206
ic_env_t* env = ic_get_env(); if (env==NULL) return;
207
history_load_from(env->history, fname, max_entries );
208
}
209
210
ic_public void ic_history_remove_last(void) {
211
ic_env_t* env = ic_get_env(); if (env==NULL) return;
212
history_remove_last(env->history);
213
}
214
215
ic_public void ic_history_add( const char* entry ) {
216
ic_env_t* env = ic_get_env(); if (env==NULL) return;
217
history_push( env->history, entry );
218
}
219
220
ic_public void ic_history_clear(void) {
221
ic_env_t* env = ic_get_env(); if (env==NULL) return;
222
history_clear(env->history);
223
}
224
225
ic_public bool ic_enable_auto_tab( bool enable ) {
226
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
227
bool prev = env->complete_autotab;
228
env->complete_autotab = enable;
229
return prev;
230
}
231
232
ic_public bool ic_enable_completion_preview( bool enable ) {
233
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
234
bool prev = env->complete_nopreview;
235
env->complete_nopreview = !enable;
236
return !prev;
237
}
238
239
ic_public bool ic_enable_multiline_indent(bool enable) {
240
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
241
bool prev = env->no_multiline_indent;
242
env->no_multiline_indent = !enable;
243
return !prev;
244
}
245
246
ic_public bool ic_enable_hint(bool enable) {
247
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
248
bool prev = env->no_hint;
249
env->no_hint = !enable;
250
return !prev;
251
}
252
253
ic_public long ic_set_hint_delay(long delay_ms) {
254
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
255
long prev = env->hint_delay;
256
env->hint_delay = (delay_ms < 0 ? 0 : (delay_ms > 5000 ? 5000 : delay_ms));
257
return prev;
258
}
259
260
ic_public void ic_set_tty_esc_delay(long initial_delay_ms, long followup_delay_ms ) {
261
ic_env_t* env = ic_get_env(); if (env==NULL) return;
262
if (env->tty == NULL) return;
263
tty_set_esc_delay(env->tty, initial_delay_ms, followup_delay_ms);
264
}
265
266
267
ic_public bool ic_enable_highlight(bool enable) {
268
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
269
bool prev = env->no_highlight;
270
env->no_highlight = !enable;
271
return !prev;
272
}
273
274
ic_public bool ic_enable_inline_help(bool enable) {
275
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
276
bool prev = env->no_help;
277
env->no_help = !enable;
278
return !prev;
279
}
280
281
ic_public bool ic_enable_brace_matching(bool enable) {
282
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
283
bool prev = env->no_bracematch;
284
env->no_bracematch = !enable;
285
return !prev;
286
}
287
288
ic_public void ic_set_matching_braces(const char* brace_pairs) {
289
ic_env_t* env = ic_get_env(); if (env==NULL) return;
290
mem_free(env->mem, env->match_braces);
291
env->match_braces = NULL;
292
if (brace_pairs != NULL) {
293
ssize_t len = ic_strlen(brace_pairs);
294
if (len > 0 && (len % 2) == 0) {
295
env->match_braces = mem_strdup(env->mem, brace_pairs);
296
}
297
}
298
}
299
300
ic_public bool ic_enable_brace_insertion(bool enable) {
301
ic_env_t* env = ic_get_env(); if (env==NULL) return false;
302
bool prev = env->no_autobrace;
303
env->no_autobrace = !enable;
304
return !prev;
305
}
306
307
ic_public void ic_set_insertion_braces(const char* brace_pairs) {
308
ic_env_t* env = ic_get_env(); if (env==NULL) return;
309
mem_free(env->mem, env->auto_braces);
310
env->auto_braces = NULL;
311
if (brace_pairs != NULL) {
312
ssize_t len = ic_strlen(brace_pairs);
313
if (len > 0 && (len % 2) == 0) {
314
env->auto_braces = mem_strdup(env->mem, brace_pairs);
315
}
316
}
317
}
318
319
ic_private const char* ic_env_get_match_braces(ic_env_t* env) {
320
return (env->match_braces == NULL ? "()[]{}" : env->match_braces);
321
}
322
323
ic_private const char* ic_env_get_auto_braces(ic_env_t* env) {
324
return (env->auto_braces == NULL ? "()[]{}\"\"''" : env->auto_braces);
325
}
326
327
ic_public void ic_set_default_highlighter(ic_highlight_fun_t* highlighter, void* arg) {
328
ic_env_t* env = ic_get_env(); if (env==NULL) return;
329
env->highlighter = highlighter;
330
env->highlighter_arg = arg;
331
}
332
333
334
ic_public void ic_free( void* p ) {
335
ic_env_t* env = ic_get_env(); if (env==NULL) return;
336
mem_free(env->mem, p);
337
}
338
339
ic_public void* ic_malloc(size_t sz) {
340
ic_env_t* env = ic_get_env(); if (env==NULL) return NULL;
341
return mem_malloc(env->mem, to_ssize_t(sz));
342
}
343
344
ic_public const char* ic_strdup( const char* s ) {
345
if (s==NULL) return NULL;
346
ic_env_t* env = ic_get_env(); if (env==NULL) return NULL;
347
ssize_t len = ic_strlen(s);
348
char* p = mem_malloc_tp_n( env->mem, char, len + 1 );
349
if (p == NULL) return NULL;
350
ic_memcpy( p, s, len );
351
p[len] = 0;
352
return p;
353
}
354
355
//-------------------------------------------------------------
356
// Terminal
357
//-------------------------------------------------------------
358
359
ic_public void ic_term_init(void) {
360
ic_env_t* env = ic_get_env(); if (env==NULL) return;
361
if (env->term==NULL) return;
362
term_start_raw(env->term);
363
}
364
365
ic_public void ic_term_done(void) {
366
ic_env_t* env = ic_get_env(); if (env==NULL) return;
367
if (env->term==NULL) return;
368
term_end_raw(env->term,false);
369
}
370
371
ic_public void ic_term_flush(void) {
372
ic_env_t* env = ic_get_env(); if (env==NULL) return;
373
if (env->term==NULL) return;
374
term_flush(env->term);
375
}
376
377
ic_public void ic_term_write(const char* s) {
378
ic_env_t* env = ic_get_env(); if (env==NULL) return;
379
if (env->term == NULL) return;
380
term_write(env->term, s);
381
}
382
383
ic_public void ic_term_writeln(const char* s) {
384
ic_env_t* env = ic_get_env(); if (env==NULL) return;
385
if (env->term == NULL) return;
386
term_writeln(env->term, s);
387
}
388
389
ic_public void ic_term_writef(const char* fmt, ...) {
390
va_list ap;
391
va_start(ap, fmt);
392
ic_term_vwritef(fmt, ap);
393
va_end(ap);
394
}
395
396
ic_public void ic_term_vwritef(const char* fmt, va_list args) {
397
ic_env_t* env = ic_get_env(); if (env==NULL) return;
398
if (env->term == NULL) return;
399
term_vwritef(env->term, fmt, args);
400
}
401
402
ic_public void ic_term_reset( void ) {
403
ic_env_t* env = ic_get_env(); if (env==NULL) return;
404
if (env->term == NULL) return;
405
term_attr_reset(env->term);
406
}
407
408
ic_public void ic_term_style( const char* style ) {
409
ic_env_t* env = ic_get_env(); if (env==NULL) return;
410
if (env->term == NULL || env->bbcode == NULL) return;
411
term_set_attr( env->term, bbcode_style(env->bbcode, style));
412
}
413
414
ic_public int ic_term_get_color_bits(void) {
415
ic_env_t* env = ic_get_env();
416
if (env==NULL || env->term==NULL) return 4;
417
return term_get_color_bits(env->term);
418
}
419
420
ic_public void ic_term_bold(bool enable) {
421
ic_env_t* env = ic_get_env(); if (env==NULL || env->term==NULL) return;
422
term_bold(env->term, enable);
423
}
424
425
ic_public void ic_term_underline(bool enable) {
426
ic_env_t* env = ic_get_env(); if (env==NULL || env->term==NULL) return;
427
term_underline(env->term, enable);
428
}
429
430
ic_public void ic_term_italic(bool enable) {
431
ic_env_t* env = ic_get_env(); if (env==NULL || env->term==NULL) return;
432
term_italic(env->term, enable);
433
}
434
435
ic_public void ic_term_reverse(bool enable) {
436
ic_env_t* env = ic_get_env(); if (env==NULL || env->term==NULL) return;
437
term_reverse(env->term, enable);
438
}
439
440
ic_public void ic_term_color_ansi(bool foreground, int ansi_color) {
441
ic_env_t* env = ic_get_env(); if (env==NULL || env->term==NULL) return;
442
ic_color_t color = color_from_ansi256(ansi_color);
443
if (foreground) { term_color(env->term, color); }
444
else { term_bgcolor(env->term, color); }
445
}
446
447
ic_public void ic_term_color_rgb(bool foreground, uint32_t hcolor) {
448
ic_env_t* env = ic_get_env(); if (env==NULL || env->term==NULL) return;
449
ic_color_t color = ic_rgb(hcolor);
450
if (foreground) { term_color(env->term, color); }
451
else { term_bgcolor(env->term, color); }
452
}
453
454
455
//-------------------------------------------------------------
456
// Readline with temporary completer and highlighter
457
//-------------------------------------------------------------
458
459
ic_public char* ic_readline_ex(const char* prompt_text,
460
ic_completer_fun_t* completer, void* completer_arg,
461
ic_highlight_fun_t* highlighter, void* highlighter_arg )
462
{
463
ic_env_t* env = ic_get_env(); if (env == NULL) return NULL;
464
// save previous
465
ic_completer_fun_t* prev_completer;
466
void* prev_completer_arg;
467
completions_get_completer(env->completions, &prev_completer, &prev_completer_arg);
468
ic_highlight_fun_t* prev_highlighter = env->highlighter;
469
void* prev_highlighter_arg = env->highlighter_arg;
470
// call with current
471
if (completer != NULL) { ic_set_default_completer(completer, completer_arg); }
472
if (highlighter != NULL) { ic_set_default_highlighter(highlighter, highlighter_arg); }
473
char* res = ic_readline(prompt_text);
474
// restore previous
475
ic_set_default_completer(prev_completer, prev_completer_arg);
476
ic_set_default_highlighter(prev_highlighter, prev_highlighter_arg);
477
return res;
478
}
479
480
481
//-------------------------------------------------------------
482
// Initialize
483
//-------------------------------------------------------------
484
485
static void ic_atexit(void);
486
487
static void ic_env_free(ic_env_t* env) {
488
if (env == NULL) return;
489
history_save(env->history);
490
history_free(env->history);
491
completions_free(env->completions);
492
bbcode_free(env->bbcode);
493
term_free(env->term);
494
tty_free(env->tty);
495
mem_free(env->mem, env->cprompt_marker);
496
mem_free(env->mem,env->prompt_marker);
497
mem_free(env->mem, env->match_braces);
498
mem_free(env->mem, env->auto_braces);
499
env->prompt_marker = NULL;
500
501
// and deallocate ourselves
502
alloc_t* mem = env->mem;
503
mem_free(mem, env);
504
505
// and finally the custom memory allocation structure
506
mem_free(mem, mem);
507
}
508
509
510
static ic_env_t* ic_env_create( ic_malloc_fun_t* _malloc, ic_realloc_fun_t* _realloc, ic_free_fun_t* _free )
511
{
512
if (_malloc == NULL) _malloc = &malloc;
513
if (_realloc == NULL) _realloc = &realloc;
514
if (_free == NULL) _free = &free;
515
// allocate
516
alloc_t* mem = (alloc_t*)_malloc(sizeof(alloc_t));
517
if (mem == NULL) return NULL;
518
mem->malloc = _malloc;
519
mem->realloc = _realloc;
520
mem->free = _free;
521
ic_env_t* env = mem_zalloc_tp(mem, ic_env_t);
522
if (env==NULL) {
523
mem->free(mem);
524
return NULL;
525
}
526
env->mem = mem;
527
528
// Initialize
529
env->tty = tty_new(env->mem, -1); // can return NULL
530
env->term = term_new(env->mem, env->tty, false, false, -1 );
531
env->history = history_new(env->mem);
532
env->completions = completions_new(env->mem);
533
env->bbcode = bbcode_new(env->mem, env->term);
534
env->hint_delay = 400;
535
536
if (env->tty == NULL || env->term==NULL ||
537
env->completions == NULL || env->history == NULL || env->bbcode == NULL ||
538
!term_is_interactive(env->term))
539
{
540
env->noedit = true;
541
}
542
env->multiline_eol = '\\';
543
544
bbcode_style_def(env->bbcode, "ic-prompt", "ansi-green" );
545
bbcode_style_def(env->bbcode, "ic-info", "ansi-darkgray" );
546
bbcode_style_def(env->bbcode, "ic-diminish", "ansi-lightgray" );
547
bbcode_style_def(env->bbcode, "ic-emphasis", "#ffffd7" );
548
bbcode_style_def(env->bbcode, "ic-hint", "ansi-darkgray" );
549
bbcode_style_def(env->bbcode, "ic-error", "#d70000" );
550
bbcode_style_def(env->bbcode, "ic-bracematch","ansi-white"); // color = #F7DC6F" );
551
552
bbcode_style_def(env->bbcode, "keyword", "#569cd6" );
553
bbcode_style_def(env->bbcode, "control", "#c586c0" );
554
bbcode_style_def(env->bbcode, "number", "#b5cea8" );
555
bbcode_style_def(env->bbcode, "string", "#ce9178" );
556
bbcode_style_def(env->bbcode, "comment", "#6A9955" );
557
bbcode_style_def(env->bbcode, "type", "darkcyan" );
558
bbcode_style_def(env->bbcode, "constant", "#569cd6" );
559
560
set_prompt_marker(env, NULL, NULL);
561
return env;
562
}
563
564
static ic_env_t* rpenv;
565
566
static void ic_atexit(void) {
567
if (rpenv != NULL) {
568
ic_env_free(rpenv);
569
rpenv = NULL;
570
}
571
}
572
573
ic_private ic_env_t* ic_get_env(void) {
574
if (rpenv==NULL) {
575
rpenv = ic_env_create( NULL, NULL, NULL );
576
if (rpenv != NULL) { atexit( &ic_atexit ); }
577
}
578
return rpenv;
579
}
580
581
ic_public void ic_init_custom_malloc( ic_malloc_fun_t* _malloc, ic_realloc_fun_t* _realloc, ic_free_fun_t* _free ) {
582
assert(rpenv == NULL);
583
if (rpenv != NULL) {
584
ic_env_free(rpenv);
585
rpenv = ic_env_create( _malloc, _realloc, _free );
586
}
587
else {
588
rpenv = ic_env_create( _malloc, _realloc, _free );
589
if (rpenv != NULL) {
590
atexit( &ic_atexit );
591
}
592
}
593
}
594
595
596