mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 05:13:10 +00:00
Make up/down cursor move up or down when in multiline mode, except if already in search mode or at the top/bottom line. Since part of this is done in script-space, this involves adding some functionality to the commandline builtin.
darcs-hash:20070921140549-75c98-ba9e83f5e6fdecae5df8f83dd863794c6af9770c.gz
This commit is contained in:
parent
607e970659
commit
d2d397d9eb
9 changed files with 308 additions and 29 deletions
|
@ -220,6 +220,8 @@ static int builtin_commandline( wchar_t **argv )
|
||||||
int tokenize = 0;
|
int tokenize = 0;
|
||||||
|
|
||||||
int cursor_mode = 0;
|
int cursor_mode = 0;
|
||||||
|
int line_mode = 0;
|
||||||
|
int search_mode = 0;
|
||||||
wchar_t *begin, *end;
|
wchar_t *begin, *end;
|
||||||
|
|
||||||
current_buffer = (wchar_t *)builtin_complete_get_temporary_buffer();
|
current_buffer = (wchar_t *)builtin_complete_get_temporary_buffer();
|
||||||
|
@ -312,6 +314,14 @@ static int builtin_commandline( wchar_t **argv )
|
||||||
L"cursor", no_argument, 0, 'C'
|
L"cursor", no_argument, 0, 'C'
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
|
{
|
||||||
|
L"line", no_argument, 0, 'L'
|
||||||
|
}
|
||||||
|
,
|
||||||
|
{
|
||||||
|
L"search-mode", no_argument, 0, 'S'
|
||||||
|
}
|
||||||
|
,
|
||||||
{
|
{
|
||||||
0, 0, 0, 0
|
0, 0, 0, 0
|
||||||
}
|
}
|
||||||
|
@ -322,7 +332,7 @@ static int builtin_commandline( wchar_t **argv )
|
||||||
|
|
||||||
int opt = wgetopt_long( argc,
|
int opt = wgetopt_long( argc,
|
||||||
argv,
|
argv,
|
||||||
L"abijpctwforhI:C",
|
L"abijpctwforhI:CLS",
|
||||||
long_options,
|
long_options,
|
||||||
&opt_index );
|
&opt_index );
|
||||||
if( opt == -1 )
|
if( opt == -1 )
|
||||||
|
@ -391,6 +401,14 @@ static int builtin_commandline( wchar_t **argv )
|
||||||
cursor_mode = 1;
|
cursor_mode = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'L':
|
||||||
|
line_mode = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
search_mode = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
builtin_print_help( argv[0], sb_out );
|
builtin_print_help( argv[0], sb_out );
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -408,7 +426,7 @@ static int builtin_commandline( wchar_t **argv )
|
||||||
/*
|
/*
|
||||||
Check for invalid switch combinations
|
Check for invalid switch combinations
|
||||||
*/
|
*/
|
||||||
if( buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode )
|
if( buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode || line_mode || search_mode )
|
||||||
{
|
{
|
||||||
sb_printf(sb_err,
|
sb_printf(sb_err,
|
||||||
BUILTIN_ERR_COMBO,
|
BUILTIN_ERR_COMBO,
|
||||||
|
@ -457,7 +475,7 @@ static int builtin_commandline( wchar_t **argv )
|
||||||
/*
|
/*
|
||||||
Check for invalid switch combinations
|
Check for invalid switch combinations
|
||||||
*/
|
*/
|
||||||
if( cursor_mode && (argc-woptind > 1) )
|
if( (search_mode || line_mode || cursor_mode) && (argc-woptind > 1) )
|
||||||
{
|
{
|
||||||
|
|
||||||
sb_append2( sb_err,
|
sb_append2( sb_err,
|
||||||
|
@ -468,7 +486,7 @@ static int builtin_commandline( wchar_t **argv )
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( (buffer_part || tokenize || cut_at_cursor) && cursor_mode )
|
if( (buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode) )
|
||||||
{
|
{
|
||||||
sb_printf( sb_err,
|
sb_printf( sb_err,
|
||||||
BUILTIN_ERR_COMBO,
|
BUILTIN_ERR_COMBO,
|
||||||
|
@ -546,6 +564,20 @@ static int builtin_commandline( wchar_t **argv )
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( line_mode )
|
||||||
|
{
|
||||||
|
int pos = reader_get_cursor_pos();
|
||||||
|
wchar_t *buff = reader_get_buffer();
|
||||||
|
sb_printf( sb_out, L"%d\n", parse_util_lineno( buff, pos ) );
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( search_mode )
|
||||||
|
{
|
||||||
|
return !reader_search_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
switch( buffer_part )
|
switch( buffer_part )
|
||||||
{
|
{
|
||||||
|
|
16
input.c
16
input.c
|
@ -134,7 +134,9 @@ static const wchar_t *name_arr[] =
|
||||||
L"execute",
|
L"execute",
|
||||||
L"beginning-of-buffer",
|
L"beginning-of-buffer",
|
||||||
L"end-of-buffer",
|
L"end-of-buffer",
|
||||||
L"repaint"
|
L"repaint",
|
||||||
|
L"up-line",
|
||||||
|
L"down-line"
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -215,7 +217,9 @@ static const wchar_t code_arr[] =
|
||||||
R_EXECUTE,
|
R_EXECUTE,
|
||||||
R_BEGINNING_OF_BUFFER,
|
R_BEGINNING_OF_BUFFER,
|
||||||
R_END_OF_BUFFER,
|
R_END_OF_BUFFER,
|
||||||
R_REPAINT
|
R_REPAINT,
|
||||||
|
R_UP_LINE,
|
||||||
|
R_DOWN_LINE
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -1242,10 +1246,10 @@ static void add_common_bindings()
|
||||||
terminfo sometimes specifies a different sequence than what
|
terminfo sometimes specifies a different sequence than what
|
||||||
keypresses actually generate
|
keypresses actually generate
|
||||||
*/
|
*/
|
||||||
add_mapping( name[i], L"\x1b[A", L"Up", L"history-search-backward" );
|
add_mapping( name[i], L"\x1b[A", L"Up", L"up-or-search" );
|
||||||
add_mapping( name[i], L"\x1b[B", L"Down", L"history-search-forward" );
|
add_mapping( name[i], L"\x1b[B", L"Down", L"down-or-search" );
|
||||||
add_terminfo_mapping( name[i], (key_up), L"Up", L"history-search-backward" );
|
add_terminfo_mapping( name[i], (key_up), L"Up", L"up-or-search" );
|
||||||
add_terminfo_mapping( name[i], (key_down), L"Down", L"history-search-forward" );
|
add_terminfo_mapping( name[i], (key_down), L"Down", L"down-or-search" );
|
||||||
|
|
||||||
add_mapping( name[i], L"\x1b[C", L"Right", L"forward-char" );
|
add_mapping( name[i], L"\x1b[C", L"Right", L"forward-char" );
|
||||||
add_mapping( name[i], L"\x1b[D", L"Left", L"backward-char" );
|
add_mapping( name[i], L"\x1b[D", L"Left", L"backward-char" );
|
||||||
|
|
4
input.h
4
input.h
|
@ -49,7 +49,9 @@ enum
|
||||||
R_EXECUTE,
|
R_EXECUTE,
|
||||||
R_BEGINNING_OF_BUFFER,
|
R_BEGINNING_OF_BUFFER,
|
||||||
R_END_OF_BUFFER,
|
R_END_OF_BUFFER,
|
||||||
R_REPAINT
|
R_REPAINT,
|
||||||
|
R_UP_LINE,
|
||||||
|
R_DOWN_LINE,
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
91
parse_util.c
91
parse_util.c
|
@ -135,6 +135,97 @@ int parse_util_lineno( const wchar_t *str, int len )
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int parse_util_get_line_from_offset( wchar_t *buff, int pos )
|
||||||
|
{
|
||||||
|
// return parse_util_lineno( buff, pos );
|
||||||
|
|
||||||
|
int i;
|
||||||
|
int count = 0;
|
||||||
|
if( pos < 0 )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( i=0; i<pos; i++ )
|
||||||
|
{
|
||||||
|
if( !buff[i] )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( buff[i] == L'\n' )
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int parse_util_get_offset_from_line( wchar_t *buff, int line )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if( line < 0 )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( line == 0 )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for( i=0;; i++ )
|
||||||
|
{
|
||||||
|
if( !buff[i] )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( buff[i] == L'\n' )
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
if( count == line )
|
||||||
|
{
|
||||||
|
return i+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_util_get_offset( wchar_t *buff, int line, int line_offset )
|
||||||
|
{
|
||||||
|
int off = parse_util_get_offset_from_line( buff, line );
|
||||||
|
int off2 = parse_util_get_offset_from_line( buff, line+1 );
|
||||||
|
int line_offset2 = line_offset;
|
||||||
|
|
||||||
|
if( off < 0 )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( off2 < 0 )
|
||||||
|
{
|
||||||
|
off2 = wcslen( buff );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( line_offset2 < 0 )
|
||||||
|
{
|
||||||
|
line_offset2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( line_offset2 >= off2-off-1 )
|
||||||
|
{
|
||||||
|
line_offset2 = off2-off-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return off + line_offset2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int parse_util_locate_cmdsubst( const wchar_t *in,
|
int parse_util_locate_cmdsubst( const wchar_t *in,
|
||||||
wchar_t **begin,
|
wchar_t **begin,
|
||||||
wchar_t **end,
|
wchar_t **end,
|
||||||
|
|
17
parse_util.h
17
parse_util.h
|
@ -93,6 +93,23 @@ void parse_util_token_extent( const wchar_t *buff,
|
||||||
*/
|
*/
|
||||||
int parse_util_lineno( const wchar_t *str, int len );
|
int parse_util_lineno( const wchar_t *str, int len );
|
||||||
|
|
||||||
|
/**
|
||||||
|
Calculate the line number of the specified cursor position
|
||||||
|
*/
|
||||||
|
int parse_util_get_line_from_offset( wchar_t *buff, int pos );
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get the offset of the first character on the specified line
|
||||||
|
*/
|
||||||
|
int parse_util_get_offset_from_line( wchar_t *buff, int line );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the total offset of the buffer for the cursor position nearest to the specified poition
|
||||||
|
*/
|
||||||
|
int parse_util_get_offset( wchar_t *buff, int line, int line_offset );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Autoload the specified file, if it exists in the specified path. Do
|
Autoload the specified file, if it exists in the specified path. Do
|
||||||
not load it multiple times unless it's timestamp changes or
|
not load it multiple times unless it's timestamp changes or
|
||||||
|
|
103
reader.c
103
reader.c
|
@ -258,6 +258,12 @@ typedef struct reader_data
|
||||||
Pointer to previous reader_data
|
Pointer to previous reader_data
|
||||||
*/
|
*/
|
||||||
struct reader_data *next;
|
struct reader_data *next;
|
||||||
|
|
||||||
|
/**
|
||||||
|
This variable keeps state on if we are in search mode, and
|
||||||
|
if yes, what mode
|
||||||
|
*/
|
||||||
|
int search_mode;
|
||||||
}
|
}
|
||||||
reader_data_t;
|
reader_data_t;
|
||||||
|
|
||||||
|
@ -681,6 +687,7 @@ void repaint()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove the previous character in the character buffer and on the
|
Remove the previous character in the character buffer and on the
|
||||||
screen using syntax highlighting, etc.
|
screen using syntax highlighting, etc.
|
||||||
|
@ -2304,11 +2311,11 @@ wchar_t *reader_readline()
|
||||||
int comp_empty=1;
|
int comp_empty=1;
|
||||||
int finished=0;
|
int finished=0;
|
||||||
struct termios old_modes;
|
struct termios old_modes;
|
||||||
int search_mode = 0;
|
|
||||||
|
|
||||||
check_size();
|
check_size();
|
||||||
sb_clear( &data->search_buff );
|
sb_clear( &data->search_buff );
|
||||||
data->buff[data->buff_len]='\0';
|
data->buff[data->buff_len]='\0';
|
||||||
|
data->search_mode = NO_SEARCH;
|
||||||
|
|
||||||
s_reset( &data->screen );
|
s_reset( &data->screen );
|
||||||
|
|
||||||
|
@ -2621,9 +2628,9 @@ wchar_t *reader_readline()
|
||||||
/* Escape was pressed */
|
/* Escape was pressed */
|
||||||
case L'\x1b':
|
case L'\x1b':
|
||||||
{
|
{
|
||||||
if( search_mode )
|
if( data->search_mode )
|
||||||
{
|
{
|
||||||
search_mode= 0;
|
data->search_mode= NO_SEARCH;
|
||||||
|
|
||||||
if( data->token_history_pos==-1 )
|
if( data->token_history_pos==-1 )
|
||||||
{
|
{
|
||||||
|
@ -2745,23 +2752,23 @@ wchar_t *reader_readline()
|
||||||
{
|
{
|
||||||
int reset = 0;
|
int reset = 0;
|
||||||
|
|
||||||
if( search_mode == NO_SEARCH )
|
if( data->search_mode == NO_SEARCH )
|
||||||
{
|
{
|
||||||
reset = 1;
|
reset = 1;
|
||||||
if( ( c == R_HISTORY_SEARCH_BACKWARD ) ||
|
if( ( c == R_HISTORY_SEARCH_BACKWARD ) ||
|
||||||
( c == R_HISTORY_SEARCH_FORWARD ) )
|
( c == R_HISTORY_SEARCH_FORWARD ) )
|
||||||
{
|
{
|
||||||
search_mode = LINE_SEARCH;
|
data->search_mode = LINE_SEARCH;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
search_mode = TOKEN_SEARCH;
|
data->search_mode = TOKEN_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
sb_append( &data->search_buff, data->buff );
|
sb_append( &data->search_buff, data->buff );
|
||||||
}
|
}
|
||||||
|
|
||||||
switch( search_mode )
|
switch( data->search_mode )
|
||||||
{
|
{
|
||||||
|
|
||||||
case LINE_SEARCH:
|
case LINE_SEARCH:
|
||||||
|
@ -2874,6 +2881,72 @@ wchar_t *reader_readline()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case R_UP_LINE:
|
||||||
|
case R_DOWN_LINE:
|
||||||
|
{
|
||||||
|
int line = parse_util_get_line_from_offset( data->buff,
|
||||||
|
data->buff_pos );
|
||||||
|
int new_line;
|
||||||
|
|
||||||
|
if( c == R_UP_LINE )
|
||||||
|
new_line = line-1;
|
||||||
|
else
|
||||||
|
new_line = line+1;
|
||||||
|
|
||||||
|
int line_count = parse_util_lineno( data->buff, data->buff_len )-1;
|
||||||
|
|
||||||
|
if( new_line >= 0 && new_line <= line_count)
|
||||||
|
{
|
||||||
|
int base_pos;
|
||||||
|
|
||||||
|
int indent_old;
|
||||||
|
int indent_new;
|
||||||
|
int old_line_offset;
|
||||||
|
int new_total_offset;
|
||||||
|
|
||||||
|
// debug( 0, L"Move up one line to %d", new_line );
|
||||||
|
|
||||||
|
base_pos = parse_util_get_offset_from_line( data->buff,
|
||||||
|
new_line );
|
||||||
|
/* debug( 0, L"Old cursor offset is %d, new base offset is %d",
|
||||||
|
data->buff_pos,
|
||||||
|
base_pos );
|
||||||
|
*/
|
||||||
|
|
||||||
|
if( data->buff_pos ==(data->buff_len) )
|
||||||
|
{
|
||||||
|
if( data->buff_pos == 0 )
|
||||||
|
{
|
||||||
|
indent_old = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
indent_old = data->indent[data->buff_pos-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( data->buff[data->buff_pos] == L'\n' )
|
||||||
|
indent_old = data->indent[data->buff_pos-1];
|
||||||
|
else
|
||||||
|
indent_old = data->indent[data->buff_pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
indent_new = data->indent[base_pos];
|
||||||
|
// debug( 0, L"Old indent %d, new indent %d", indent_old, indent_new );
|
||||||
|
|
||||||
|
old_line_offset = data->buff_pos - parse_util_get_offset_from_line( data->buff,
|
||||||
|
line );
|
||||||
|
new_total_offset = parse_util_get_offset( data->buff, new_line, old_line_offset - 4*(indent_new-indent_old));
|
||||||
|
data->buff_pos = new_total_offset;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Other, if a normal character, we add it to the command */
|
/* Other, if a normal character, we add it to the command */
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
@ -2900,9 +2973,10 @@ wchar_t *reader_readline()
|
||||||
if( (c != R_HISTORY_SEARCH_BACKWARD) &&
|
if( (c != R_HISTORY_SEARCH_BACKWARD) &&
|
||||||
(c != R_HISTORY_SEARCH_FORWARD) &&
|
(c != R_HISTORY_SEARCH_FORWARD) &&
|
||||||
(c != R_HISTORY_TOKEN_SEARCH_BACKWARD) &&
|
(c != R_HISTORY_TOKEN_SEARCH_BACKWARD) &&
|
||||||
(c != R_HISTORY_TOKEN_SEARCH_FORWARD) )
|
(c != R_HISTORY_TOKEN_SEARCH_FORWARD) &&
|
||||||
|
(c != R_NULL) )
|
||||||
{
|
{
|
||||||
search_mode = 0;
|
data->search_mode = NO_SEARCH;
|
||||||
sb_clear( &data->search_buff );
|
sb_clear( &data->search_buff );
|
||||||
history_reset();
|
history_reset();
|
||||||
data->token_history_pos=-1;
|
data->token_history_pos=-1;
|
||||||
|
@ -2929,6 +3003,17 @@ wchar_t *reader_readline()
|
||||||
return finished ? data->buff : 0;
|
return finished ? data->buff : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int reader_search_mode()
|
||||||
|
{
|
||||||
|
if( !data )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!data->search_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Read non-interactively. Read input from stdin without displaying
|
Read non-interactively. Read input from stdin without displaying
|
||||||
the prompt, using syntax highlighting. This is used for reading
|
the prompt, using syntax highlighting. This is used for reading
|
||||||
|
|
8
reader.h
8
reader.h
|
@ -174,4 +174,12 @@ int reader_exit_forced();
|
||||||
*/
|
*/
|
||||||
int reader_shell_test( wchar_t *b );
|
int reader_shell_test( wchar_t *b );
|
||||||
|
|
||||||
|
/**
|
||||||
|
Test whether the interactive reader is in search mode.
|
||||||
|
|
||||||
|
\return o if not in search mode, 1 if in search mode and -1 if not in interactive mode
|
||||||
|
*/
|
||||||
|
int reader_search_mode();
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
23
share/functions/down-or-search.fish
Normal file
23
share/functions/down-or-search.fish
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
function down-or-search
|
||||||
|
# If we are already in search mode, continue
|
||||||
|
if commandline --search-mode
|
||||||
|
commandline -f history-search-forward
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# We are not in search mode.
|
||||||
|
# If we are on the bottom line, start search mode,
|
||||||
|
# otherwise move down
|
||||||
|
set lineno (commandline -L)
|
||||||
|
set line_count (commandline|wc -l)
|
||||||
|
|
||||||
|
echo on line $lineno of $line_count >&2
|
||||||
|
|
||||||
|
switch $lineno
|
||||||
|
case $line_count
|
||||||
|
commandline -f history-search-forward
|
||||||
|
|
||||||
|
case '*'
|
||||||
|
commandline -f down-line
|
||||||
|
end
|
||||||
|
end
|
17
share/functions/up-or-search.fish
Normal file
17
share/functions/up-or-search.fish
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
function up-or-search
|
||||||
|
# If we are already in search mode, continue
|
||||||
|
if commandline --search-mode
|
||||||
|
commandline -f history-search-backward
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
set lineno (commandline -L)
|
||||||
|
|
||||||
|
switch $lineno
|
||||||
|
case 1
|
||||||
|
commandline -f history-search-backward
|
||||||
|
|
||||||
|
case '*'
|
||||||
|
commandline -f up-line
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue