Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4408953
readline.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
43 KB
Referenced Files
None
Subscribers
None
readline.c
View Options
// TODO: cleanup module, make cleaner
#include
<compat.h>
// IWYU pragma: keep
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>
#include
<stdbool.h>
#include
<ctype.h>
#include
<uthash.h>
#include
<uv.h>
#ifdef _WIN32
#include
<conio.h>
#define WIN32_LEAN_AND_MEAN
#include
<windows.h>
#include
<io.h>
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#else
#include
<termios.h>
#include
<unistd.h>
#include
<sys/select.h>
#include
<sys/ioctl.h>
#endif
#include
"ant.h"
#include
"errors.h"
#include
"runtime.h"
#include
"internal.h"
#include
"descriptors.h"
#include
"tty_ctrl.h"
#include
"silver/engine.h"
#include
"gc/modules.h"
#include
"modules/events.h"
#include
"modules/readline.h"
#include
"modules/process.h"
#include
"modules/symbol.h"
#define MAX_LINE_LENGTH 4096
#define MAX_HISTORY 1000
#define DEFAULT_PROMPT "> "
#define DEFAULT_HISTORY_SIZE 30
#define DEFAULT_TAB_SIZE 8
typedef
struct
{
char
**
lines
;
int
count
;
int
capacity
;
int
current
;
}
rl_history_t
;
typedef
struct
rl_interface
{
uint64_t
id
;
ant_value_t
input_stream
;
ant_value_t
output_stream
;
ant_value_t
completer
;
ant_value_t
js_obj
;
char
*
prompt
;
char
*
active_prompt
;
char
*
line_buffer
;
int
line_pos
;
int
line_len
;
rl_history_t
history
;
bool
terminal
;
bool
paused
;
bool
closed
;
bool
reading
;
int
history_size
;
bool
remove_history_duplicates
;
int
crlf_delay
;
int
escape_code_timeout
;
int
tab_size
;
ant_value_t
pending_question_resolve
;
ant_value_t
pending_question_reject
;
uv_tty_t
tty_in
;
uv_tty_t
tty_out
;
bool
tty_initialized
;
int
escape_state
;
char
escape_buf
[
16
];
int
escape_len
;
int
last_render_rows
;
UT_hash_handle
hh
;
#ifndef _WIN32
struct
termios
saved_termios
;
bool
raw_mode
;
uv_signal_t
sigint_watcher
;
bool
sigint_watcher_active
;
#endif
}
rl_interface_t
;
static
uint64_t
next_interface_id
=
1
;
static
rl_interface_t
*
interfaces
=
NULL
;
static
ant_value_t
g_rl_async_iter_proto
=
0
;
static
ant_value_t
g_rl_interface_proto
=
0
;
static
const
char
*
rl_render_prompt
(
const
rl_interface_t
*
iface
)
{
if
(
!
iface
)
return
""
;
return
iface
->
active_prompt
?
iface
->
active_prompt
:
iface
->
prompt
;
}
static
void
rl_set_active_prompt
(
rl_interface_t
*
iface
,
const
char
*
prompt
)
{
if
(
!
iface
)
return
;
free
(
iface
->
active_prompt
);
iface
->
active_prompt
=
prompt
?
strdup
(
prompt
)
:
NULL
;
}
static
void
rl_clear_active_prompt
(
rl_interface_t
*
iface
)
{
if
(
!
iface
)
return
;
free
(
iface
->
active_prompt
);
iface
->
active_prompt
=
NULL
;
}
static
void
rl_history_init
(
rl_history_t
*
hist
,
int
capacity
)
{
hist
->
capacity
=
capacity
>
0
?
capacity
:
DEFAULT_HISTORY_SIZE
;
hist
->
lines
=
calloc
(
hist
->
capacity
,
sizeof
(
char
*
));
hist
->
count
=
0
;
hist
->
current
=
-1
;
}
static
void
rl_history_remove_at
(
rl_history_t
*
hist
,
int
index
)
{
if
(
!
hist
||
index
<
0
||
index
>=
hist
->
count
)
return
;
free
(
hist
->
lines
[
index
]);
if
(
index
<
hist
->
count
-
1
)
memmove
(
hist
->
lines
+
index
,
hist
->
lines
+
index
+
1
,
sizeof
(
char
*
)
*
(
size_t
)(
hist
->
count
-
index
-
1
)
);
hist
->
count
--
;
}
static
void
rl_history_add
(
rl_history_t
*
hist
,
const
char
*
line
,
bool
remove_duplicates
)
{
int
duplicate_index
=
-1
;
if
(
!
line
||
line
[
0
]
==
'\0'
)
return
;
if
(
!
remove_duplicates
&&
hist
->
count
>
0
&&
strcmp
(
hist
->
lines
[
hist
->
count
-
1
],
line
)
==
0
)
return
;
if
(
remove_duplicates
)
{
for
(
int
i
=
0
;
i
<
hist
->
count
;
i
++
)
{
if
(
strcmp
(
hist
->
lines
[
i
],
line
)
!=
0
)
continue
;
duplicate_index
=
i
;
break
;
}}
if
(
duplicate_index
>=
0
)
rl_history_remove_at
(
hist
,
duplicate_index
);
if
(
hist
->
count
>=
hist
->
capacity
)
rl_history_remove_at
(
hist
,
0
);
hist
->
lines
[
hist
->
count
++
]
=
strdup
(
line
);
hist
->
current
=
hist
->
count
;
}
static
const
char
*
rl_history_prev
(
rl_history_t
*
hist
)
{
if
(
hist
->
count
==
0
)
return
NULL
;
if
(
hist
->
current
>
0
)
hist
->
current
--
;
return
hist
->
lines
[
hist
->
current
];
}
static
const
char
*
rl_history_next
(
rl_history_t
*
hist
)
{
if
(
hist
->
count
==
0
)
return
NULL
;
if
(
hist
->
current
<
hist
->
count
-
1
)
{
hist
->
current
++
;
return
hist
->
lines
[
hist
->
current
];
}
hist
->
current
=
hist
->
count
;
return
""
;
}
static
void
rl_history_free
(
rl_history_t
*
hist
)
{
if
(
hist
->
lines
)
{
for
(
int
i
=
0
;
i
<
hist
->
count
;
i
++
)
{
free
(
hist
->
lines
[
i
]);
}
free
(
hist
->
lines
);
hist
->
lines
=
NULL
;
}
hist
->
count
=
0
;
hist
->
current
=
-1
;
}
#ifndef _WIN32
static
void
enter_raw_mode
(
rl_interface_t
*
iface
)
{
if
(
iface
->
raw_mode
)
return
;
struct
termios
raw
;
if
(
tcgetattr
(
STDIN_FILENO
,
&
iface
->
saved_termios
)
==
-1
)
return
;
raw
=
iface
->
saved_termios
;
cfmakeraw
(
&
raw
);
raw
.
c_lflag
|=
ISIG
;
raw
.
c_oflag
|=
OPOST
|
ONLCR
;
if
(
tcsetattr
(
STDIN_FILENO
,
TCSANOW
,
&
raw
)
==
-1
)
return
;
iface
->
raw_mode
=
true
;
}
static
void
exit_raw_mode
(
rl_interface_t
*
iface
)
{
if
(
!
iface
->
raw_mode
)
return
;
tcsetattr
(
STDIN_FILENO
,
TCSANOW
,
&
iface
->
saved_termios
);
iface
->
raw_mode
=
false
;
}
#endif
static
void
write_output
(
rl_interface_t
*
iface
,
const
char
*
str
)
{
fputs
(
str
,
stdout
);
fflush
(
stdout
);
}
static
int
get_terminal_cols
(
void
)
{
int
cols
=
80
;
#ifndef _WIN32
struct
winsize
ws
;
if
(
ioctl
(
STDOUT_FILENO
,
TIOCGWINSZ
,
&
ws
)
==
0
&&
ws
.
ws_col
>
0
)
{
cols
=
ws
.
ws_col
;
}
#else
CONSOLE_SCREEN_BUFFER_INFO
csbi
;
if
(
GetConsoleScreenBufferInfo
(
GetStdHandle
(
STD_OUTPUT_HANDLE
),
&
csbi
))
{
cols
=
csbi
.
srWindow
.
Right
-
csbi
.
srWindow
.
Left
+
1
;
}
#endif
return
cols
>
0
?
cols
:
80
;
}
static
void
move_cursor_to_line_start
(
rl_interface_t
*
iface
,
int
cols
)
{
int
prompt_len
=
(
int
)
strlen
(
rl_render_prompt
(
iface
));
int
cursor_cols
=
prompt_len
+
iface
->
line_pos
;
int
cursor_row
=
cursor_cols
/
cols
;
if
(
cursor_cols
>
0
&&
cursor_cols
%
cols
==
0
)
cursor_row
--
;
if
(
cursor_row
>
0
)
{
char
move_buf
[
32
];
snprintf
(
move_buf
,
sizeof
(
move_buf
),
"
\033
[%dA"
,
cursor_row
);
write_output
(
iface
,
move_buf
);
}
write_output
(
iface
,
"
\r
"
);
}
static
void
clear_line_display
(
rl_interface_t
*
iface
)
{
int
cols
=
get_terminal_cols
();
int
prompt_len
=
(
int
)
strlen
(
rl_render_prompt
(
iface
));
int
line_cols
=
prompt_len
+
iface
->
line_len
;
int
current_rows
=
line_cols
>
0
?
(
line_cols
-
1
)
/
cols
+
1
:
1
;
int
rows
=
iface
->
last_render_rows
>
current_rows
?
iface
->
last_render_rows
:
current_rows
;
move_cursor_to_line_start
(
iface
,
cols
);
for
(
int
i
=
0
;
i
<
rows
;
i
++
)
{
write_output
(
iface
,
"
\033
[K"
);
if
(
i
<
rows
-
1
)
{
write_output
(
iface
,
"
\033
[B
\r
"
);
}
}
for
(
int
i
=
0
;
i
<
rows
-
1
;
i
++
)
{
write_output
(
iface
,
"
\033
[A"
);
}
write_output
(
iface
,
"
\r
"
);
}
static
void
refresh_line
(
rl_interface_t
*
iface
)
{
char
buf
[
MAX_LINE_LENGTH
+
256
];
int
cols
=
get_terminal_cols
();
const
char
*
prompt
=
rl_render_prompt
(
iface
);
clear_line_display
(
iface
);
snprintf
(
buf
,
sizeof
(
buf
),
"%s%s"
,
prompt
,
iface
->
line_buffer
);
write_output
(
iface
,
buf
);
int
prompt_len
=
(
int
)
strlen
(
prompt
);
int
end_cols
=
prompt_len
+
iface
->
line_len
;
int
end_row
=
end_cols
>
0
?
end_cols
/
cols
:
0
;
int
cursor_cols
=
prompt_len
+
iface
->
line_pos
;
int
cursor_row
=
cursor_cols
>
0
?
cursor_cols
/
cols
:
0
;
int
cursor_col
=
cursor_cols
>
0
?
cursor_cols
%
cols
:
0
;
int
up_rows
=
end_row
-
cursor_row
;
if
(
up_rows
>
0
)
{
char
move_buf
[
32
];
snprintf
(
move_buf
,
sizeof
(
move_buf
),
"
\033
[%dA"
,
up_rows
);
write_output
(
iface
,
move_buf
);
}
write_output
(
iface
,
"
\r
"
);
if
(
cursor_col
>
0
)
{
char
move_buf
[
32
];
snprintf
(
move_buf
,
sizeof
(
move_buf
),
"
\033
[%dC"
,
cursor_col
);
write_output
(
iface
,
move_buf
);
}
iface
->
last_render_rows
=
end_cols
>
0
?
end_cols
/
cols
+
1
:
1
;
}
static
rl_interface_t
*
find_interface_by_id
(
uint64_t
id
)
{
rl_interface_t
*
iface
=
NULL
;
HASH_FIND
(
hh
,
interfaces
,
&
id
,
sizeof
(
uint64_t
),
iface
);
return
iface
;
}
static
void
handle_history_up
(
rl_interface_t
*
iface
)
{
const
char
*
hist_line
=
rl_history_prev
(
&
iface
->
history
);
if
(
hist_line
)
{
strcpy
(
iface
->
line_buffer
,
hist_line
);
iface
->
line_len
=
(
int
)
strlen
(
iface
->
line_buffer
);
iface
->
line_pos
=
iface
->
line_len
;
refresh_line
(
iface
);
}
}
static
void
handle_history_down
(
rl_interface_t
*
iface
)
{
const
char
*
hist_line
=
rl_history_next
(
&
iface
->
history
);
if
(
hist_line
)
{
strcpy
(
iface
->
line_buffer
,
hist_line
);
iface
->
line_len
=
(
int
)
strlen
(
iface
->
line_buffer
);
iface
->
line_pos
=
iface
->
line_len
;
refresh_line
(
iface
);
}
}
static
void
handle_char_input
(
rl_interface_t
*
iface
,
char
c
)
{
if
(
iface
->
line_len
<
MAX_LINE_LENGTH
-
1
)
{
memmove
(
iface
->
line_buffer
+
iface
->
line_pos
+
1
,
iface
->
line_buffer
+
iface
->
line_pos
,
iface
->
line_len
-
iface
->
line_pos
+
1
);
iface
->
line_buffer
[
iface
->
line_pos
]
=
c
;
iface
->
line_pos
++
;
iface
->
line_len
++
;
if
(
iface
->
line_pos
==
iface
->
line_len
)
{
int
cols
=
get_terminal_cols
();
int
prompt_len
=
(
int
)
strlen
(
rl_render_prompt
(
iface
));
int
total_cols
=
prompt_len
+
iface
->
line_len
;
int
rows
=
total_cols
>
0
?
total_cols
/
cols
+
1
:
1
;
if
(
rows
>
iface
->
last_render_rows
)
iface
->
last_render_rows
=
rows
;
printf
(
"%c"
,
c
);
fflush
(
stdout
);
}
else
refresh_line
(
iface
);
}
}
static
void
handle_backspace
(
rl_interface_t
*
iface
)
{
if
(
iface
->
line_pos
>
0
)
{
if
(
iface
->
line_pos
==
iface
->
line_len
)
{
iface
->
line_pos
--
;
iface
->
line_len
--
;
iface
->
line_buffer
[
iface
->
line_len
]
=
'\0'
;
write_output
(
iface
,
"
\b
\b
"
);
int
cols
=
get_terminal_cols
();
int
prompt_len
=
(
int
)
strlen
(
rl_render_prompt
(
iface
));
int
total_cols
=
prompt_len
+
iface
->
line_len
;
iface
->
last_render_rows
=
total_cols
>
0
?
total_cols
/
cols
+
1
:
1
;
return
;
}
memmove
(
iface
->
line_buffer
+
iface
->
line_pos
-
1
,
iface
->
line_buffer
+
iface
->
line_pos
,
iface
->
line_len
-
iface
->
line_pos
+
1
);
iface
->
line_pos
--
;
iface
->
line_len
--
;
refresh_line
(
iface
);
}
}
static
void
handle_delete
(
rl_interface_t
*
iface
)
{
if
(
iface
->
line_pos
<
iface
->
line_len
)
{
memmove
(
iface
->
line_buffer
+
iface
->
line_pos
,
iface
->
line_buffer
+
iface
->
line_pos
+
1
,
iface
->
line_len
-
iface
->
line_pos
);
iface
->
line_len
--
;
refresh_line
(
iface
);
}
}
static
void
handle_escape_sequence
(
rl_interface_t
*
iface
,
const
char
*
seq
,
int
len
)
{
if
(
len
>=
2
&&
seq
[
0
]
==
'['
)
{
switch
(
seq
[
1
])
{
case
'A'
:
handle_history_up
(
iface
);
break
;
case
'B'
:
handle_history_down
(
iface
);
break
;
case
'C'
:
if
(
iface
->
line_pos
<
iface
->
line_len
)
{
iface
->
line_pos
++
;
printf
(
"
\033
[C"
);
fflush
(
stdout
);
}
break
;
case
'D'
:
if
(
iface
->
line_pos
>
0
)
{
iface
->
line_pos
--
;
printf
(
"
\033
[D"
);
fflush
(
stdout
);
}
break
;
case
'H'
:
iface
->
line_pos
=
0
;
refresh_line
(
iface
);
break
;
case
'F'
:
iface
->
line_pos
=
iface
->
line_len
;
refresh_line
(
iface
);
break
;
case
'3'
:
if
(
len
>=
3
&&
seq
[
2
]
==
'~'
)
{
handle_delete
(
iface
);
}
break
;
}}
}
static
void
alloc_buffer
(
uv_handle_t
*
handle
,
size_t
suggested_size
,
uv_buf_t
*
buf
)
{
buf
->
base
=
malloc
(
suggested_size
);
buf
->
len
=
suggested_size
;
}
static
void
emit_event
(
ant_t
*
js
,
rl_interface_t
*
iface
,
const
char
*
event_type
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
!
iface
||
vtype
(
iface
->
js_obj
)
==
T_UNDEF
)
return
;
eventemitter_emit_args
(
js
,
iface
->
js_obj
,
event_type
,
args
,
nargs
);
}
static
ant_value_t
get_history_array
(
ant_t
*
js
,
rl_interface_t
*
iface
)
{
ant_value_t
arr
=
js_mkarr
(
js
);
for
(
int
i
=
0
;
i
<
iface
->
history
.
count
;
i
++
)
js_arr_push
(
js
,
arr
,
js_mkstr
(
js
,
iface
->
history
.
lines
[
i
],
strlen
(
iface
->
history
.
lines
[
i
]))
);
return
arr
;
}
static
void
emit_history_event
(
ant_t
*
js
,
rl_interface_t
*
iface
)
{
ant_value_t
history_arr
=
get_history_array
(
js
,
iface
);
emit_event
(
js
,
iface
,
"history"
,
&
history_arr
,
1
);
}
static
void
stop_reading
(
rl_interface_t
*
iface
)
{
if
(
!
iface
->
reading
)
return
;
uv_read_stop
((
uv_stream_t
*
)
&
iface
->
tty_in
);
iface
->
reading
=
false
;
#ifndef _WIN32
if
(
iface
->
sigint_watcher_active
)
{
uv_signal_stop
(
&
iface
->
sigint_watcher
);
iface
->
sigint_watcher_active
=
false
;
}
#endif
}
static
bool
rl_has_event_listener
(
ant_t
*
js
,
rl_interface_t
*
iface
,
const
char
*
event_type
)
{
if
(
!
iface
||
!
event_type
||
vtype
(
iface
->
js_obj
)
==
T_UNDEF
)
return
false
;
return
eventemitter_listener_count
(
js
,
iface
->
js_obj
,
event_type
)
>
0
;
}
static
bool
rl_add_listener
(
ant_t
*
js
,
rl_interface_t
*
iface
,
const
char
*
event_type
,
ant_value_t
listener
,
bool
once
)
{
if
(
!
iface
||
!
event_type
||
vtype
(
iface
->
js_obj
)
==
T_UNDEF
)
return
false
;
return
eventemitter_add_listener
(
js
,
iface
->
js_obj
,
event_type
,
listener
,
once
);
}
static
void
rl_remove_listener
(
ant_t
*
js
,
rl_interface_t
*
iface
,
const
char
*
event_type
,
ant_value_t
listener
)
{
if
(
!
iface
||
!
event_type
||
vtype
(
iface
->
js_obj
)
==
T_UNDEF
)
return
;
eventemitter_remove_listener
(
js
,
iface
->
js_obj
,
event_type
,
listener
);
}
static
ant_value_t
rl_async_iter_state
(
ant_t
*
js
,
ant_value_t
iterator
)
{
ant_value_t
state
=
is_object_type
(
iterator
)
?
js_get_slot
(
iterator
,
SLOT_DATA
)
:
js_mkundef
();
return
is_object_type
(
state
)
?
state
:
js_mkundef
();
}
static
ant_value_t
rl_async_iter_queue
(
ant_t
*
js
,
ant_value_t
state
,
const
char
*
queue_key
)
{
ant_value_t
queue
=
is_object_type
(
state
)
?
js_get
(
js
,
state
,
queue_key
)
:
js_mkundef
();
return
vtype
(
queue
)
==
T_ARR
?
queue
:
js_mkundef
();
}
static
ant_offset_t
rl_async_iter_queue_head
(
ant_t
*
js
,
ant_value_t
state
,
const
char
*
head_key
)
{
ant_value_t
head
=
is_object_type
(
state
)
?
js_get
(
js
,
state
,
head_key
)
:
js_mkundef
();
return
vtype
(
head
)
==
T_NUM
?
(
ant_offset_t
)
js_getnum
(
head
)
:
0
;
}
static
void
rl_async_iter_set_queue_head
(
ant_t
*
js
,
ant_value_t
state
,
const
char
*
head_key
,
ant_offset_t
head
)
{
if
(
is_object_type
(
state
))
js_set
(
js
,
state
,
head_key
,
js_mknum
((
double
)
head
));
}
static
void
rl_close_interface
(
ant_t
*
js
,
rl_interface_t
*
iface
)
{
if
(
!
iface
||
iface
->
closed
)
return
;
stop_reading
(
iface
);
if
(
iface
->
tty_initialized
)
{
uv_close
((
uv_handle_t
*
)
&
iface
->
tty_in
,
NULL
);
#ifndef _WIN32
if
(
!
iface
->
sigint_watcher_active
&&
uv_is_active
((
uv_handle_t
*
)
&
iface
->
sigint_watcher
))
{
uv_close
((
uv_handle_t
*
)
&
iface
->
sigint_watcher
,
NULL
);
}
exit_raw_mode
(
iface
);
#endif
iface
->
tty_initialized
=
false
;
}
iface
->
closed
=
true
;
emit_event
(
js
,
iface
,
"close"
,
NULL
,
0
);
}
static
void
process_line
(
ant_t
*
js
,
rl_interface_t
*
iface
)
{
char
*
line
=
strdup
(
iface
->
line_buffer
);
rl_history_add
(
&
iface
->
history
,
line
,
iface
->
remove_history_duplicates
);
emit_history_event
(
js
,
iface
);
rl_clear_active_prompt
(
iface
);
ant_value_t
line_val
=
js_mkstr
(
js
,
line
,
strlen
(
line
));
emit_event
(
js
,
iface
,
"line"
,
&
line_val
,
1
);
if
(
vtype
(
iface
->
pending_question_resolve
)
==
T_FUNC
)
{
sv_vm_call
(
js
->
vm
,
js
,
iface
->
pending_question_resolve
,
js_mkundef
(),
&
line_val
,
1
,
NULL
,
false
);
iface
->
pending_question_resolve
=
js_mkundef
();
iface
->
pending_question_reject
=
js_mkundef
();
}
iface
->
line_buffer
[
0
]
=
'\0'
;
iface
->
line_pos
=
0
;
iface
->
line_len
=
0
;
free
(
line
);
}
static
void
feed_escape
(
rl_interface_t
*
iface
,
char
c
)
{
iface
->
escape_buf
[
iface
->
escape_len
++
]
=
c
;
if
(
iface
->
escape_state
==
1
)
{
iface
->
escape_state
=
(
c
==
'['
||
c
==
'O'
)
?
2
:
0
;
if
(
!
iface
->
escape_state
)
iface
->
escape_len
=
0
;
return
;
}
bool
done
=
(
c
>=
'A'
&&
c
<=
'Z'
)
||
c
==
'~'
;
if
(
done
)
handle_escape_sequence
(
iface
,
iface
->
escape_buf
,
iface
->
escape_len
);
if
(
done
||
iface
->
escape_len
>=
15
)
{
iface
->
escape_state
=
0
;
iface
->
escape_len
=
0
;
}
}
static
void
process_byte
(
ant_t
*
js
,
rl_interface_t
*
iface
,
char
c
)
{
if
(
iface
->
escape_state
>
0
)
{
feed_escape
(
iface
,
c
);
return
;
}
if
(
c
==
27
)
{
iface
->
escape_state
=
1
;
iface
->
escape_len
=
0
;
return
;
}
switch
(
c
)
{
case
'\r'
:
case
'\n'
:
putchar
(
'\n'
);
fflush
(
stdout
);
process_line
(
js
,
iface
);
break
;
case
127
:
case
8
:
handle_backspace
(
iface
);
break
;
case
4
:
if
(
iface
->
line_len
==
0
)
rl_close_interface
(
js
,
iface
);
else
handle_delete
(
iface
);
break
;
case
1
:
iface
->
line_pos
=
0
;
refresh_line
(
iface
);
break
;
case
5
:
iface
->
line_pos
=
iface
->
line_len
;
refresh_line
(
iface
);
break
;
case
11
:
iface
->
line_buffer
[
iface
->
line_pos
]
=
'\0'
;
iface
->
line_len
=
iface
->
line_pos
;
refresh_line
(
iface
);
break
;
case
21
:
iface
->
line_buffer
[
0
]
=
'\0'
;
iface
->
line_pos
=
0
;
iface
->
line_len
=
0
;
refresh_line
(
iface
);
break
;
case
12
:
printf
(
"
\033
[2J
\033
[H"
);
refresh_line
(
iface
);
break
;
default
:
if
(
c
>=
32
&&
c
<
127
)
handle_char_input
(
iface
,
c
);
break
;
}
}
static
void
on_stdin_read
(
uv_stream_t
*
stream
,
ssize_t
nread
,
const
uv_buf_t
*
buf
)
{
rl_interface_t
*
iface
=
(
rl_interface_t
*
)
stream
->
data
;
ant_t
*
js
=
rt
->
js
;
if
(
!
iface
||
iface
->
closed
||
iface
->
paused
)
goto
cleanup
;
if
(
nread
<
0
)
{
if
(
nread
==
UV_EOF
)
rl_close_interface
(
js
,
iface
);
goto
cleanup
;
}
for
(
ssize_t
i
=
0
;
i
<
nread
;
i
++
)
{
process_byte
(
js
,
iface
,
buf
->
base
[
i
]);
if
(
iface
->
closed
)
break
;
}
if
(
iface
->
closed
)
stop_reading
(
iface
);
cleanup
:
free
(
buf
->
base
);
}
#ifndef _WIN32
static
void
on_sigint
(
uv_signal_t
*
handle
,
int
signum
)
{
rl_interface_t
*
iface
=
(
rl_interface_t
*
)
handle
->
data
;
ant_t
*
js
=
rt
->
js
;
if
(
rl_has_event_listener
(
js
,
iface
,
"SIGINT"
))
{
emit_event
(
js
,
iface
,
"SIGINT"
,
NULL
,
0
);
}
else
if
(
process_has_event_listeners
(
"SIGINT"
))
{
ant_value_t
sig_arg
=
js_mkstr
(
js
,
"SIGINT"
,
6
);
emit_process_event
(
"SIGINT"
,
&
sig_arg
,
1
);
}
else
{
uv_signal_stop
(
handle
);
raise
(
SIGINT
);
}
}
#endif
static
void
start_reading
(
rl_interface_t
*
iface
)
{
if
(
iface
->
reading
||
iface
->
closed
)
return
;
if
(
!
iface
->
tty_initialized
)
{
uv_loop_t
*
loop
=
uv_default_loop
();
int
is_tty
=
uv_guess_handle
(
STDIN_FILENO
)
==
UV_TTY
;
if
(
uv_tty_init
(
loop
,
&
iface
->
tty_in
,
STDIN_FILENO
,
1
)
!=
0
)
return
;
if
(
is_tty
)
{
#ifndef _WIN32
enter_raw_mode
(
iface
);
uv_signal_init
(
loop
,
&
iface
->
sigint_watcher
);
iface
->
sigint_watcher
.
data
=
iface
;
uv_signal_start
(
&
iface
->
sigint_watcher
,
on_sigint
,
SIGINT
);
iface
->
sigint_watcher_active
=
true
;
#endif
}
iface
->
tty_in
.
data
=
iface
;
iface
->
tty_initialized
=
true
;
}
iface
->
reading
=
true
;
uv_read_start
((
uv_stream_t
*
)
&
iface
->
tty_in
,
alloc_buffer
,
on_stdin_read
);
}
static
rl_interface_t
*
get_interface
(
ant_t
*
js
,
ant_value_t
this_obj
)
{
ant_value_t
id_val
=
js_get
(
js
,
this_obj
,
"_rl_id"
);
if
(
vtype
(
id_val
)
!=
T_NUM
)
return
NULL
;
uint64_t
id
=
(
uint64_t
)
js_getnum
(
id_val
);
rl_interface_t
*
iface
=
NULL
;
HASH_FIND
(
hh
,
interfaces
,
&
id
,
sizeof
(
uint64_t
),
iface
);
return
iface
;
}
static
ant_value_t
rl_interface_close
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
||
iface
->
closed
)
return
js_mkundef
();
rl_close_interface
(
js
,
iface
);
return
js_mkundef
();
}
static
ant_value_t
rl_interface_pause
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
)
return
js_mkerr
(
js
,
"Invalid Interface"
);
if
(
!
iface
->
paused
)
{
iface
->
paused
=
true
;
stop_reading
(
iface
);
emit_event
(
js
,
iface
,
"pause"
,
NULL
,
0
);
}
return
this_obj
;
}
static
ant_value_t
rl_interface_resume
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
)
return
js_mkerr
(
js
,
"Invalid Interface"
);
if
(
iface
->
paused
)
{
iface
->
paused
=
false
;
start_reading
(
iface
);
emit_event
(
js
,
iface
,
"resume"
,
NULL
,
0
);
}
return
this_obj
;
}
static
ant_value_t
rl_interface_prompt
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
||
iface
->
closed
)
return
js_mkundef
();
if
(
iface
->
paused
)
{
iface
->
paused
=
false
;
emit_event
(
js
,
iface
,
"resume"
,
NULL
,
0
);
}
bool
preserve_cursor
=
false
;
if
(
nargs
>
0
)
preserve_cursor
=
js_truthy
(
js
,
args
[
0
]);
if
(
!
preserve_cursor
)
{
iface
->
line_buffer
[
0
]
=
'\0'
;
iface
->
line_pos
=
0
;
iface
->
line_len
=
0
;
}
write_output
(
iface
,
rl_render_prompt
(
iface
));
if
(
iface
->
line_len
>
0
)
{
write_output
(
iface
,
iface
->
line_buffer
);
}
start_reading
(
iface
);
return
js_mkundef
();
}
static
ant_value_t
rl_interface_set_prompt
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
)
return
js_mkerr
(
js
,
"Invalid Interface"
);
if
(
nargs
<
1
)
return
js_mkundef
();
char
*
new_prompt
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
new_prompt
)
{
free
(
iface
->
prompt
);
iface
->
prompt
=
strdup
(
new_prompt
);
}
return
js_mkundef
();
}
static
ant_value_t
rl_interface_get_prompt
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
)
return
js_mkerr
(
js
,
"Invalid Interface"
);
return
js_mkstr
(
js
,
iface
->
prompt
,
strlen
(
iface
->
prompt
));
}
static
void
process_key_sequence
(
rl_interface_t
*
iface
,
const
char
*
name
,
bool
ctrl
,
bool
meta
,
bool
shift
)
{
(
void
)
meta
;
(
void
)
shift
;
if
(
!
name
)
return
;
if
(
strcmp
(
name
,
"return"
)
==
0
||
strcmp
(
name
,
"enter"
)
==
0
)
{
printf
(
"
\n
"
);
fflush
(
stdout
);
}
else
if
(
strcmp
(
name
,
"backspace"
)
==
0
)
{
if
(
iface
->
line_pos
>
0
)
{
memmove
(
iface
->
line_buffer
+
iface
->
line_pos
-
1
,
iface
->
line_buffer
+
iface
->
line_pos
,
iface
->
line_len
-
iface
->
line_pos
+
1
);
iface
->
line_pos
--
;
iface
->
line_len
--
;
}
}
else
if
(
strcmp
(
name
,
"delete"
)
==
0
)
{
if
(
iface
->
line_pos
<
iface
->
line_len
)
{
memmove
(
iface
->
line_buffer
+
iface
->
line_pos
,
iface
->
line_buffer
+
iface
->
line_pos
+
1
,
iface
->
line_len
-
iface
->
line_pos
);
iface
->
line_len
--
;
}
}
else
if
(
strcmp
(
name
,
"left"
)
==
0
)
{
if
(
iface
->
line_pos
>
0
)
iface
->
line_pos
--
;
}
else
if
(
strcmp
(
name
,
"right"
)
==
0
)
{
if
(
iface
->
line_pos
<
iface
->
line_len
)
iface
->
line_pos
++
;
}
else
if
(
strcmp
(
name
,
"home"
)
==
0
||
(
ctrl
&&
strcmp
(
name
,
"a"
)
==
0
))
{
iface
->
line_pos
=
0
;
}
else
if
(
strcmp
(
name
,
"end"
)
==
0
||
(
ctrl
&&
strcmp
(
name
,
"e"
)
==
0
))
{
iface
->
line_pos
=
iface
->
line_len
;
}
else
if
(
ctrl
&&
strcmp
(
name
,
"u"
)
==
0
)
{
iface
->
line_buffer
[
0
]
=
'\0'
;
iface
->
line_pos
=
0
;
iface
->
line_len
=
0
;
}
else
if
(
ctrl
&&
strcmp
(
name
,
"k"
)
==
0
)
{
iface
->
line_buffer
[
iface
->
line_pos
]
=
'\0'
;
iface
->
line_len
=
iface
->
line_pos
;
}
}
static
ant_value_t
rl_interface_write
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
||
iface
->
closed
)
return
js_mkundef
();
if
(
iface
->
paused
)
{
iface
->
paused
=
false
;
emit_event
(
js
,
iface
,
"resume"
,
NULL
,
0
);
}
if
(
nargs
>=
2
&&
is_special_object
(
args
[
1
]))
{
ant_value_t
key
=
args
[
1
];
ant_value_t
name_val
=
js_get
(
js
,
key
,
"name"
);
ant_value_t
ctrl_val
=
js_get
(
js
,
key
,
"ctrl"
);
ant_value_t
meta_val
=
js_get
(
js
,
key
,
"meta"
);
ant_value_t
shift_val
=
js_get
(
js
,
key
,
"shift"
);
char
*
name
=
(
vtype
(
name_val
)
==
T_STR
)
?
js_getstr
(
js
,
name_val
,
NULL
)
:
NULL
;
bool
ctrl
=
js_truthy
(
js
,
ctrl_val
);
bool
meta
=
js_truthy
(
js
,
meta_val
);
bool
shift
=
js_truthy
(
js
,
shift_val
);
if
(
name
)
{
process_key_sequence
(
iface
,
name
,
ctrl
,
meta
,
shift
);
return
js_mkundef
();
}
}
if
(
nargs
<
1
||
vtype
(
args
[
0
])
==
T_NULL
||
vtype
(
args
[
0
])
==
T_UNDEF
)
{
return
js_mkundef
();
}
size_t
len
;
char
*
data
=
js_getstr
(
js
,
args
[
0
],
&
len
);
if
(
!
data
)
return
js_mkundef
();
for
(
size_t
i
=
0
;
i
<
len
&&
iface
->
line_len
<
MAX_LINE_LENGTH
-
1
;
i
++
)
{
char
c
=
data
[
i
];
if
(
c
==
'\n'
||
c
==
'\r'
)
{
process_line
(
js
,
iface
);
}
else
{
memmove
(
iface
->
line_buffer
+
iface
->
line_pos
+
1
,
iface
->
line_buffer
+
iface
->
line_pos
,
iface
->
line_len
-
iface
->
line_pos
+
1
);
iface
->
line_buffer
[
iface
->
line_pos
]
=
c
;
iface
->
line_pos
++
;
iface
->
line_len
++
;
}
}
return
js_mkundef
();
}
static
ant_value_t
rl_interface_line_getter
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
)
return
js_mkundef
();
return
js_mkstr
(
js
,
iface
->
line_buffer
,
strlen
(
iface
->
line_buffer
));
}
static
ant_value_t
rl_interface_cursor_getter
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
)
return
js_mknum
(
0
);
return
js_mknum
((
double
)
iface
->
line_pos
);
}
static
ant_value_t
rl_interface_question_callback
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
||
iface
->
closed
)
return
js_mkundef
();
if
(
nargs
<
2
)
return
js_mkerr
(
js
,
"question requires query and callback"
);
size_t
query_len
;
char
*
query
=
js_getstr
(
js
,
args
[
0
],
&
query_len
);
if
(
!
query
)
return
js_mkerr
(
js
,
"query must be a string"
);
int
t
=
vtype
(
args
[
1
]);
if
(
t
!=
T_FUNC
&&
t
!=
T_CFUNC
)
{
return
js_mkerr
(
js
,
"callback must be a function"
);
}
rl_set_active_prompt
(
iface
,
query
);
write_output
(
iface
,
rl_render_prompt
(
iface
));
if
(
!
rl_add_listener
(
js
,
iface
,
"line"
,
args
[
1
],
true
))
return
js_mkerr
(
js
,
"listener must be a function"
);
return
js_mkundef
();
}
static
ant_value_t
rl_interface_question_promise
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
||
iface
->
closed
)
return
js_mkerr
(
js
,
"Interface is closed"
);
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"question requires a query string"
);
size_t
query_len
;
char
*
query
=
js_getstr
(
js
,
args
[
0
],
&
query_len
);
if
(
!
query
)
return
js_mkerr
(
js
,
"query must be a string"
);
ant_value_t
promise
=
js_mkpromise
(
js
);
rl_set_active_prompt
(
iface
,
query
);
write_output
(
iface
,
rl_render_prompt
(
iface
));
iface
->
pending_question_resolve
=
js_get
(
js
,
promise
,
"_resolve"
);
iface
->
pending_question_reject
=
js_get
(
js
,
promise
,
"_reject"
);
return
promise
;
}
static
ant_value_t
rl_interface_get_cursor_pos
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
)
{
ant_value_t
result
=
js_mkobj
(
js
);
js_set
(
js
,
result
,
"rows"
,
js_mknum
(
0
));
js_set
(
js
,
result
,
"cols"
,
js_mknum
(
0
));
return
result
;
}
int
prompt_len
=
(
int
)
strlen
(
rl_render_prompt
(
iface
));
int
total_cols
=
prompt_len
+
iface
->
line_pos
;
int
cols
=
80
;
#ifndef _WIN32
struct
winsize
ws
;
if
(
ioctl
(
STDOUT_FILENO
,
TIOCGWINSZ
,
&
ws
)
==
0
&&
ws
.
ws_col
>
0
)
{
cols
=
ws
.
ws_col
;
}
#else
CONSOLE_SCREEN_BUFFER_INFO
csbi
;
if
(
GetConsoleScreenBufferInfo
(
GetStdHandle
(
STD_OUTPUT_HANDLE
),
&
csbi
))
{
cols
=
csbi
.
srWindow
.
Right
-
csbi
.
srWindow
.
Left
+
1
;
}
#endif
int
rows
=
total_cols
/
cols
;
int
col_pos
=
total_cols
%
cols
;
ant_value_t
result
=
js_mkobj
(
js
);
js_set
(
js
,
result
,
"rows"
,
js_mknum
((
double
)
rows
));
js_set
(
js
,
result
,
"cols"
,
js_mknum
((
double
)
col_pos
));
return
result
;
}
static
ant_value_t
rl_interface_closed_getter
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
if
(
!
iface
)
return
js_true
;
return
js_bool
(
iface
->
closed
);
}
static
void
free_interface
(
rl_interface_t
*
iface
)
{
if
(
!
iface
)
return
;
HASH_DEL
(
interfaces
,
iface
);
free
(
iface
->
prompt
);
free
(
iface
->
active_prompt
);
free
(
iface
->
line_buffer
);
rl_history_free
(
&
iface
->
history
);
free
(
iface
);
}
static
ant_value_t
rl_clear_line
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
int
dir
=
0
;
if
(
!
tty_ctrl_parse_clear_line_dir
(
args
,
nargs
,
1
,
&
dir
))
return
js_false
;
size_t
seq_len
=
0
;
const
char
*
seq
=
tty_ctrl_clear_line_seq
(
dir
,
&
seq_len
);
return
tty_ctrl_bool_result
(
js
,
tty_ctrl_write_stream
(
stdout
,
seq
,
seq_len
,
true
));
}
static
ant_value_t
rl_clear_screen_down
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
size_t
seq_len
=
0
;
const
char
*
seq
=
tty_ctrl_clear_screen_down_seq
(
&
seq_len
);
return
tty_ctrl_bool_result
(
js
,
tty_ctrl_write_stream
(
stdout
,
seq
,
seq_len
,
true
));
}
static
ant_value_t
rl_cursor_to
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
tty_ctrl_cursor_to_args_t
parsed
;
if
(
!
tty_ctrl_parse_cursor_to_args
(
args
,
nargs
,
1
,
2
,
&
parsed
))
return
js_false
;
char
seq
[
64
];
size_t
seq_len
=
0
;
bool
ok
=
tty_ctrl_build_cursor_to
(
seq
,
sizeof
(
seq
),
parsed
.
x
,
parsed
.
has_y
,
parsed
.
y
,
&
seq_len
);
if
(
!
ok
)
return
js_false
;
return
tty_ctrl_bool_result
(
js
,
tty_ctrl_write_stream
(
stdout
,
seq
,
seq_len
,
true
));
}
static
ant_value_t
rl_move_cursor
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
int
dx
=
0
;
int
dy
=
0
;
if
(
!
tty_ctrl_parse_move_cursor_args
(
args
,
nargs
,
1
,
2
,
&
dx
,
&
dy
))
return
js_false
;
bool
ok
=
true
;
if
(
dx
!=
0
)
{
char
seq_x
[
32
];
size_t
len_x
=
0
;
ok
=
tty_ctrl_build_move_cursor_axis
(
seq_x
,
sizeof
(
seq_x
),
dx
,
true
,
&
len_x
);
if
(
ok
)
ok
=
tty_ctrl_write_stream
(
stdout
,
seq_x
,
len_x
,
false
);
}
if
(
ok
&&
dy
!=
0
)
{
char
seq_y
[
32
];
size_t
len_y
=
0
;
ok
=
tty_ctrl_build_move_cursor_axis
(
seq_y
,
sizeof
(
seq_y
),
dy
,
false
,
&
len_y
);
if
(
ok
)
ok
=
tty_ctrl_write_stream
(
stdout
,
seq_y
,
len_y
,
false
);
}
if
(
!
ok
)
return
js_false
;
return
tty_ctrl_bool_result
(
js
,
fflush
(
stdout
)
==
0
);
}
static
ant_value_t
rl_emit_keypress_events
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
>
0
)
{
ant_value_t
stdin_obj
=
js_get
(
js
,
js_get
(
js
,
js_glob
(
js
),
"process"
),
"stdin"
);
if
(
stdin_obj
!=
args
[
0
])
{
return
js_mkerr
(
js
,
"emitKeypressEvents only supports process.stdin"
);
}
}
process_enable_keypress_events
();
return
js_mkundef
();
}
bool
has_active_readline_interfaces
(
void
)
{
rl_interface_t
*
iface
,
*
tmp
;
HASH_ITER
(
hh
,
interfaces
,
iface
,
tmp
)
{
if
(
!
iface
->
closed
&&
iface
->
reading
)
return
true
;
}
return
false
;
}
static
void
rl_async_iter_compact_queue
(
ant_t
*
js
,
ant_value_t
state
,
const
char
*
queue_key
,
const
char
*
head_key
)
{
ant_value_t
queue
=
rl_async_iter_queue
(
js
,
state
,
queue_key
);
ant_offset_t
head
=
rl_async_iter_queue_head
(
js
,
state
,
head_key
);
ant_offset_t
len
=
vtype
(
queue
)
==
T_ARR
?
js_arr_len
(
js
,
queue
)
:
0
;
ant_value_t
compact
=
0
;
if
(
vtype
(
queue
)
!=
T_ARR
||
head
==
0
)
return
;
if
(
head
>=
len
)
{
js_set
(
js
,
state
,
queue_key
,
js_mkarr
(
js
));
js_set
(
js
,
state
,
head_key
,
js_mknum
(
0
));
return
;
}
if
(
head
<=
32
&&
head
*
2
<
len
)
return
;
compact
=
js_mkarr
(
js
);
for
(
ant_offset_t
i
=
head
;
i
<
len
;
i
++
)
js_arr_push
(
js
,
compact
,
js_arr_get
(
js
,
queue
,
i
));
js_set
(
js
,
state
,
queue_key
,
compact
);
js_set
(
js
,
state
,
head_key
,
js_mknum
(
0
));
}
static
void
rl_async_iter_queue_push
(
ant_t
*
js
,
ant_value_t
state
,
const
char
*
queue_key
,
ant_value_t
value
)
{
ant_value_t
queue
=
rl_async_iter_queue
(
js
,
state
,
queue_key
);
if
(
vtype
(
queue
)
==
T_ARR
)
js_arr_push
(
js
,
queue
,
value
);
}
static
ant_value_t
rl_async_iter_queue_shift
(
ant_t
*
js
,
ant_value_t
state
,
const
char
*
queue_key
,
const
char
*
head_key
)
{
ant_value_t
queue
=
rl_async_iter_queue
(
js
,
state
,
queue_key
);
ant_offset_t
head
=
rl_async_iter_queue_head
(
js
,
state
,
head_key
);
ant_offset_t
len
=
vtype
(
queue
)
==
T_ARR
?
js_arr_len
(
js
,
queue
)
:
0
;
ant_value_t
value
=
js_mkundef
();
if
(
vtype
(
queue
)
!=
T_ARR
||
head
>=
len
)
return
js_mkundef
();
value
=
js_arr_get
(
js
,
queue
,
head
);
rl_async_iter_set_queue_head
(
js
,
state
,
head_key
,
head
+
1
);
rl_async_iter_compact_queue
(
js
,
state
,
queue_key
,
head_key
);
return
value
;
}
static
void
rl_async_iter_cleanup
(
ant_t
*
js
,
ant_value_t
state
)
{
ant_value_t
iface_obj
=
is_object_type
(
state
)
?
js_get
(
js
,
state
,
"iface"
)
:
js_mkundef
();
rl_interface_t
*
iface
=
get_interface
(
js
,
iface_obj
);
if
(
!
iface
)
return
;
rl_remove_listener
(
js
,
iface
,
"line"
,
js_get
(
js
,
state
,
"onLine"
));
rl_remove_listener
(
js
,
iface
,
"close"
,
js_get
(
js
,
state
,
"onClose"
));
}
static
void
rl_async_iter_finish
(
ant_t
*
js
,
ant_value_t
state
)
{
if
(
!
is_object_type
(
state
))
return
;
js_set
(
js
,
state
,
"done"
,
js_true
);
rl_async_iter_cleanup
(
js
,
state
);
for
(;;)
{
ant_value_t
pending
=
rl_async_iter_queue_shift
(
js
,
state
,
"pending"
,
"pendingHead"
);
if
(
vtype
(
pending
)
!=
T_PROMISE
)
break
;
js_resolve_promise
(
js
,
pending
,
js_iter_result
(
js
,
false
,
js_mkundef
()));
}
}
static
ant_value_t
rl_async_iter_on_line
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
rl_async_iter_state
(
js
,
js_getcurrentfunc
(
js
));
ant_value_t
line
=
nargs
>
0
?
args
[
0
]
:
js_mkundef
();
ant_value_t
pending
=
0
;
if
(
!
is_object_type
(
state
))
return
js_mkundef
();
if
(
js_truthy
(
js
,
js_get
(
js
,
state
,
"done"
)))
return
js_mkundef
();
pending
=
rl_async_iter_queue_shift
(
js
,
state
,
"pending"
,
"pendingHead"
);
if
(
vtype
(
pending
)
==
T_PROMISE
)
{
js_resolve_promise
(
js
,
pending
,
js_iter_result
(
js
,
true
,
line
));
}
else
rl_async_iter_queue_push
(
js
,
state
,
"buffer"
,
line
);
return
js_mkundef
();
}
static
ant_value_t
rl_async_iter_on_close
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
rl_async_iter_state
(
js
,
js_getcurrentfunc
(
js
));
rl_async_iter_finish
(
js
,
state
);
return
js_mkundef
();
}
static
ant_value_t
rl_async_iter_next
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
rl_async_iter_state
(
js
,
js_getthis
(
js
));
ant_value_t
promise
=
js_mkpromise
(
js
);
ant_value_t
value
=
0
;
if
(
!
is_object_type
(
state
))
{
js_resolve_promise
(
js
,
promise
,
js_iter_result
(
js
,
false
,
js_mkundef
()));
return
promise
;
}
value
=
rl_async_iter_queue_shift
(
js
,
state
,
"buffer"
,
"bufferHead"
);
if
(
vtype
(
value
)
!=
T_UNDEF
)
{
js_resolve_promise
(
js
,
promise
,
js_iter_result
(
js
,
true
,
value
));
return
promise
;
}
if
(
js_truthy
(
js
,
js_get
(
js
,
state
,
"done"
)))
{
js_resolve_promise
(
js
,
promise
,
js_iter_result
(
js
,
false
,
js_mkundef
()));
return
promise
;
}
rl_async_iter_queue_push
(
js
,
state
,
"pending"
,
promise
);
return
promise
;
}
static
ant_value_t
rl_async_iter_return
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
rl_async_iter_state
(
js
,
js_getthis
(
js
));
ant_value_t
promise
=
js_mkpromise
(
js
);
if
(
!
is_object_type
(
state
))
{
js_resolve_promise
(
js
,
promise
,
js_iter_result
(
js
,
false
,
js_mkundef
()));
return
promise
;
}
if
(
!
js_truthy
(
js
,
js_get
(
js
,
state
,
"done"
)))
{
ant_value_t
iface_obj
=
js_get
(
js
,
state
,
"iface"
);
rl_interface_t
*
iface
=
get_interface
(
js
,
iface_obj
);
if
(
iface
&&
!
iface
->
closed
)
{
ant_value_t
old_this
=
js_getthis
(
js
);
js_setthis
(
js
,
iface_obj
);
rl_interface_close
(
js
,
NULL
,
0
);
js_setthis
(
js
,
old_this
);
}
else
rl_async_iter_finish
(
js
,
state
);
}
js_resolve_promise
(
js
,
promise
,
js_iter_result
(
js
,
false
,
js_mkundef
()));
return
promise
;
}
static
ant_value_t
rl_get_async_iter_proto
(
ant_t
*
js
)
{
if
(
is_object_type
(
g_rl_async_iter_proto
))
return
g_rl_async_iter_proto
;
g_rl_async_iter_proto
=
js_mkobj
(
js
);
js_set
(
js
,
g_rl_async_iter_proto
,
"next"
,
js_mkfun
(
rl_async_iter_next
));
js_set
(
js
,
g_rl_async_iter_proto
,
"return"
,
js_mkfun
(
rl_async_iter_return
));
js_set_sym
(
js
,
g_rl_async_iter_proto
,
get_asyncIterator_sym
(),
js_mkfun
(
sym_this_cb
));
js_set_sym
(
js
,
g_rl_async_iter_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"AsyncIterator"
,
13
));
return
g_rl_async_iter_proto
;
}
static
ant_value_t
rl_interface_async_iterator
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
rl_interface_t
*
iface
=
get_interface
(
js
,
this_obj
);
ant_value_t
iterator
=
0
;
ant_value_t
state
=
0
;
ant_value_t
on_line
=
0
;
ant_value_t
on_close
=
0
;
if
(
!
iface
)
return
js_mkerr
(
js
,
"Invalid Interface"
);
iterator
=
js_mkobj
(
js
);
state
=
js_mkobj
(
js
);
js_set_proto_init
(
iterator
,
rl_get_async_iter_proto
(
js
));
js_set_slot_wb
(
js
,
iterator
,
SLOT_DATA
,
state
);
js_set
(
js
,
state
,
"iface"
,
this_obj
);
js_set
(
js
,
state
,
"buffer"
,
js_mkarr
(
js
));
js_set
(
js
,
state
,
"bufferHead"
,
js_mknum
(
0
));
js_set
(
js
,
state
,
"pending"
,
js_mkarr
(
js
));
js_set
(
js
,
state
,
"pendingHead"
,
js_mknum
(
0
));
js_set
(
js
,
state
,
"done"
,
js_bool
(
iface
->
closed
));
if
(
!
iface
->
closed
)
{
on_line
=
js_heavy_mkfun
(
js
,
rl_async_iter_on_line
,
state
);
on_close
=
js_heavy_mkfun
(
js
,
rl_async_iter_on_close
,
state
);
js_set
(
js
,
state
,
"onLine"
,
on_line
);
js_set
(
js
,
state
,
"onClose"
,
on_close
);
if
(
!
rl_add_listener
(
js
,
iface
,
"line"
,
on_line
,
false
)
||
!
rl_add_listener
(
js
,
iface
,
"close"
,
on_close
,
false
))
{
rl_remove_listener
(
js
,
iface
,
"line"
,
on_line
);
rl_remove_listener
(
js
,
iface
,
"close"
,
on_close
);
return
js_mkerr
(
js
,
"listener must be a function"
);
}}
return
iterator
;
}
static
ant_value_t
rl_get_interface_proto
(
ant_t
*
js
)
{
if
(
is_object_type
(
g_rl_interface_proto
))
return
g_rl_interface_proto
;
g_rl_interface_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
g_rl_interface_proto
,
eventemitter_prototype
(
js
));
js_set
(
js
,
g_rl_interface_proto
,
"close"
,
js_mkfun
(
rl_interface_close
));
js_set
(
js
,
g_rl_interface_proto
,
"pause"
,
js_mkfun
(
rl_interface_pause
));
js_set
(
js
,
g_rl_interface_proto
,
"resume"
,
js_mkfun
(
rl_interface_resume
));
js_set
(
js
,
g_rl_interface_proto
,
"prompt"
,
js_mkfun
(
rl_interface_prompt
));
js_set
(
js
,
g_rl_interface_proto
,
"setPrompt"
,
js_mkfun
(
rl_interface_set_prompt
));
js_set
(
js
,
g_rl_interface_proto
,
"getPrompt"
,
js_mkfun
(
rl_interface_get_prompt
));
js_set
(
js
,
g_rl_interface_proto
,
"write"
,
js_mkfun
(
rl_interface_write
));
js_set
(
js
,
g_rl_interface_proto
,
"question"
,
js_mkfun
(
rl_interface_question_callback
));
js_set
(
js
,
g_rl_interface_proto
,
"getCursorPos"
,
js_mkfun
(
rl_interface_get_cursor_pos
));
js_set_getter_desc
(
js
,
g_rl_interface_proto
,
"line"
,
4
,
js_mkfun
(
rl_interface_line_getter
),
JS_DESC_E
|
JS_DESC_C
);
js_set_getter_desc
(
js
,
g_rl_interface_proto
,
"cursor"
,
6
,
js_mkfun
(
rl_interface_cursor_getter
),
JS_DESC_E
|
JS_DESC_C
);
js_set_getter_desc
(
js
,
g_rl_interface_proto
,
"closed"
,
6
,
js_mkfun
(
rl_interface_closed_getter
),
JS_DESC_E
|
JS_DESC_C
);
js_set_sym
(
js
,
g_rl_interface_proto
,
get_asyncIterator_sym
(),
js_mkfun
(
rl_interface_async_iterator
));
js_set_sym
(
js
,
g_rl_interface_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"Interface"
,
9
));
return
g_rl_interface_proto
;
}
static
ant_value_t
rl_create_interface
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"createInterface requires options"
);
ant_value_t
options
=
args
[
0
];
if
(
!
is_special_object
(
options
))
return
js_mkerr
(
js
,
"options must be an object"
);
rl_interface_t
*
iface
=
calloc
(
1
,
sizeof
(
rl_interface_t
));
if
(
!
iface
)
return
js_mkerr
(
js
,
"out of memory"
);
iface
->
id
=
next_interface_id
++
;
iface
->
prompt
=
strdup
(
DEFAULT_PROMPT
);
iface
->
active_prompt
=
NULL
;
iface
->
line_buffer
=
calloc
(
MAX_LINE_LENGTH
,
1
);
iface
->
line_pos
=
0
;
iface
->
line_len
=
0
;
iface
->
paused
=
false
;
iface
->
closed
=
false
;
iface
->
reading
=
false
;
iface
->
pending_question_resolve
=
js_mkundef
();
iface
->
pending_question_reject
=
js_mkundef
();
iface
->
tty_initialized
=
false
;
iface
->
escape_state
=
0
;
iface
->
escape_len
=
0
;
iface
->
last_render_rows
=
1
;
iface
->
js_obj
=
js_mkundef
();
#ifndef _WIN32
iface
->
raw_mode
=
false
;
#endif
iface
->
input_stream
=
js_get
(
js
,
options
,
"input"
);
iface
->
output_stream
=
js_get
(
js
,
options
,
"output"
);
ant_value_t
terminal_val
=
js_get
(
js
,
options
,
"terminal"
);
iface
->
terminal
=
terminal_val
==
js_true
||
vtype
(
terminal_val
)
==
T_UNDEF
;
ant_value_t
history_size_val
=
js_get
(
js
,
options
,
"historySize"
);
iface
->
history_size
=
(
vtype
(
history_size_val
)
==
T_NUM
)
?
(
int
)
js_getnum
(
history_size_val
)
:
DEFAULT_HISTORY_SIZE
;
ant_value_t
remove_dup_val
=
js_get
(
js
,
options
,
"removeHistoryDuplicates"
);
iface
->
remove_history_duplicates
=
js_truthy
(
js
,
remove_dup_val
);
ant_value_t
prompt_val
=
js_get
(
js
,
options
,
"prompt"
);
if
(
vtype
(
prompt_val
)
==
T_STR
)
{
free
(
iface
->
prompt
);
iface
->
prompt
=
strdup
(
js_getstr
(
js
,
prompt_val
,
NULL
));
}
ant_value_t
crlf_delay_val
=
js_get
(
js
,
options
,
"crlfDelay"
);
iface
->
crlf_delay
=
(
vtype
(
crlf_delay_val
)
==
T_NUM
)
?
(
int
)
js_getnum
(
crlf_delay_val
)
:
100
;
if
(
iface
->
crlf_delay
<
100
)
iface
->
crlf_delay
=
100
;
ant_value_t
tab_size_val
=
js_get
(
js
,
options
,
"tabSize"
);
iface
->
tab_size
=
(
vtype
(
tab_size_val
)
==
T_NUM
)
?
(
int
)
js_getnum
(
tab_size_val
)
:
DEFAULT_TAB_SIZE
;
if
(
iface
->
tab_size
<
1
)
iface
->
tab_size
=
1
;
ant_value_t
completer_val
=
js_get
(
js
,
options
,
"completer"
);
int
ctype
=
vtype
(
completer_val
);
iface
->
completer
=
(
ctype
==
T_FUNC
||
ctype
==
T_CFUNC
)
?
completer_val
:
js_mkundef
();
ant_value_t
history_val
=
js_get
(
js
,
options
,
"history"
);
if
(
is_special_object
(
history_val
))
{
ant_value_t
len_val
=
js_get
(
js
,
history_val
,
"length"
);
int
len
=
(
vtype
(
len_val
)
==
T_NUM
)
?
(
int
)
js_getnum
(
len_val
)
:
0
;
rl_history_init
(
&
iface
->
history
,
iface
->
history_size
);
for
(
int
i
=
0
;
i
<
len
;
i
++
)
{
char
key
[
16
];
snprintf
(
key
,
sizeof
(
key
),
"%d"
,
i
);
ant_value_t
item
=
js_get
(
js
,
history_val
,
key
);
if
(
vtype
(
item
)
==
T_STR
)
{
char
*
line
=
js_getstr
(
js
,
item
,
NULL
);
if
(
line
)
rl_history_add
(
&
iface
->
history
,
line
,
false
);
}
}
}
else
rl_history_init
(
&
iface
->
history
,
iface
->
history_size
);
HASH_ADD
(
hh
,
interfaces
,
id
,
sizeof
(
uint64_t
),
iface
);
ant_value_t
obj
=
js_mkobj
(
js
);
js_set_proto_init
(
obj
,
rl_get_interface_proto
(
js
));
iface
->
js_obj
=
obj
;
js_set
(
js
,
obj
,
"_rl_id"
,
js_mknum
((
double
)
iface
->
id
));
js_set
(
js
,
obj
,
"terminal"
,
js_bool
(
iface
->
terminal
));
start_reading
(
iface
);
return
obj
;
}
static
ant_value_t
rl_create_interface_promises
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
iface_obj
=
rl_create_interface
(
js
,
args
,
nargs
);
if
(
vtype
(
iface_obj
)
==
T_ERR
)
return
iface_obj
;
js_set
(
js
,
iface_obj
,
"question"
,
js_mkfun
(
rl_interface_question_promise
));
return
iface_obj
;
}
ant_value_t
readline_library
(
ant_t
*
js
)
{
ant_value_t
lib
=
js_mkobj
(
js
);
js_set
(
js
,
lib
,
"createInterface"
,
js_mkfun
(
rl_create_interface
));
js_set
(
js
,
lib
,
"clearLine"
,
js_mkfun
(
rl_clear_line
));
js_set
(
js
,
lib
,
"clearScreenDown"
,
js_mkfun
(
rl_clear_screen_down
));
js_set
(
js
,
lib
,
"cursorTo"
,
js_mkfun
(
rl_cursor_to
));
js_set
(
js
,
lib
,
"moveCursor"
,
js_mkfun
(
rl_move_cursor
));
js_set
(
js
,
lib
,
"emitKeypressEvents"
,
js_mkfun
(
rl_emit_keypress_events
));
js_set_sym
(
js
,
lib
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"readline"
,
8
));
return
lib
;
}
ant_value_t
readline_promises_library
(
ant_t
*
js
)
{
ant_value_t
lib
=
js_mkobj
(
js
);
js_set
(
js
,
lib
,
"createInterface"
,
js_mkfun
(
rl_create_interface_promises
));
js_set
(
js
,
lib
,
"clearLine"
,
js_mkfun
(
rl_clear_line
));
js_set
(
js
,
lib
,
"clearScreenDown"
,
js_mkfun
(
rl_clear_screen_down
));
js_set
(
js
,
lib
,
"cursorTo"
,
js_mkfun
(
rl_cursor_to
));
js_set
(
js
,
lib
,
"moveCursor"
,
js_mkfun
(
rl_move_cursor
));
js_set
(
js
,
lib
,
"emitKeypressEvents"
,
js_mkfun
(
rl_emit_keypress_events
));
js_set_sym
(
js
,
lib
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"readline/promises"
,
17
));
return
lib
;
}
void
gc_mark_readline
(
ant_t
*
js
,
gc_mark_fn
mark
)
{
if
(
g_rl_async_iter_proto
)
mark
(
js
,
g_rl_async_iter_proto
);
if
(
g_rl_interface_proto
)
mark
(
js
,
g_rl_interface_proto
);
rl_interface_t
*
iface
,
*
tmp
;
HASH_ITER
(
hh
,
interfaces
,
iface
,
tmp
)
{
mark
(
js
,
iface
->
input_stream
);
mark
(
js
,
iface
->
output_stream
);
mark
(
js
,
iface
->
completer
);
mark
(
js
,
iface
->
js_obj
);
mark
(
js
,
iface
->
pending_question_resolve
);
mark
(
js
,
iface
->
pending_question_reject
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, May 1, 7:00 PM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
541615
Default Alt Text
readline.c (43 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment