2020-06-17 16:35:06 +03:00
/*
* Copyright ( c ) 2020 , the SerenityOS developers .
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-06-17 16:35:06 +03:00
*/
# include "AST.h"
# include "Shell.h"
2020-09-01 12:43:32 +03:00
# include <AK/MemoryStream.h>
2020-08-22 12:18:39 +03:00
# include <AK/ScopeGuard.h>
2021-03-12 19:29:37 +03:00
# include <AK/ScopedValueRollback.h>
2020-06-17 16:35:06 +03:00
# include <AK/String.h>
# include <AK/StringBuilder.h>
# include <AK/URL.h>
2020-08-22 12:18:39 +03:00
# include <LibCore/EventLoop.h>
2020-06-17 16:35:06 +03:00
# include <LibCore/File.h>
2021-03-12 19:29:37 +03:00
# include <errno.h>
2021-01-12 21:21:59 +03:00
# include <fcntl.h>
2020-08-08 12:18:07 +03:00
# include <signal.h>
2021-03-12 19:29:37 +03:00
# include <unistd.h>
2020-06-17 16:35:06 +03:00
2020-12-30 14:14:15 +03:00
void AK : : Formatter < Shell : : AST : : Command > : : format ( FormatBuilder & builder , const Shell : : AST : : Command & value )
2020-10-26 01:57:19 +03:00
{
if ( m_sign_mode ! = FormatBuilder : : SignMode : : Default )
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-10-26 01:57:19 +03:00
if ( m_alternative_form )
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-10-26 01:57:19 +03:00
if ( m_zero_pad )
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-10-26 01:57:19 +03:00
if ( m_mode ! = Mode : : Default & & m_mode ! = Mode : : String )
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-12-30 14:14:15 +03:00
if ( m_width . has_value ( ) )
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-12-30 14:14:15 +03:00
if ( m_precision . has_value ( ) )
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-10-26 01:57:19 +03:00
2020-10-28 16:50:42 +03:00
if ( value . argv . is_empty ( ) ) {
builder . put_literal ( " (ShellInternal) " ) ;
} else {
bool first = true ;
for ( auto & arg : value . argv ) {
if ( ! first )
builder . put_literal ( " " ) ;
first = false ;
builder . put_literal ( arg ) ;
}
}
for ( auto & redir : value . redirections ) {
builder . put_padding ( ' ' , 1 ) ;
if ( redir . is_path_redirection ( ) ) {
auto path_redir = ( const Shell : : AST : : PathRedirection * ) & redir ;
builder . put_i64 ( path_redir - > fd ) ;
switch ( path_redir - > direction ) {
case Shell : : AST : : PathRedirection : : Read :
builder . put_literal ( " < " ) ;
break ;
case Shell : : AST : : PathRedirection : : Write :
builder . put_literal ( " > " ) ;
break ;
case Shell : : AST : : PathRedirection : : WriteAppend :
builder . put_literal ( " >> " ) ;
break ;
case Shell : : AST : : PathRedirection : : ReadWrite :
builder . put_literal ( " <> " ) ;
break ;
}
builder . put_literal ( path_redir - > path ) ;
} else if ( redir . is_fd_redirection ( ) ) {
auto * fdredir = ( const Shell : : AST : : FdRedirection * ) & redir ;
builder . put_i64 ( fdredir - > new_fd ) ;
builder . put_literal ( " > " ) ;
builder . put_i64 ( fdredir - > old_fd ) ;
} else if ( redir . is_close_redirection ( ) ) {
auto close_redir = ( const Shell : : AST : : CloseRedirection * ) & redir ;
builder . put_i64 ( close_redir - > fd ) ;
builder . put_literal ( " >&- " ) ;
} else {
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-10-28 16:50:42 +03:00
}
2020-10-26 01:57:19 +03:00
}
if ( ! value . next_chain . is_empty ( ) ) {
for ( auto & command : value . next_chain ) {
switch ( command . action ) {
case Shell : : AST : : NodeWithAction : : And :
builder . put_literal ( " && " ) ;
break ;
case Shell : : AST : : NodeWithAction : : Or :
builder . put_literal ( " || " ) ;
break ;
case Shell : : AST : : NodeWithAction : : Sequence :
builder . put_literal ( " ; " ) ;
break ;
}
builder . put_literal ( " ( " ) ;
builder . put_literal ( command . node - > class_name ( ) ) ;
builder . put_literal ( " ...) " ) ;
}
}
if ( ! value . should_wait )
builder . put_literal ( " & " ) ;
}
2020-10-01 17:43:01 +03:00
namespace Shell : : AST {
2020-06-17 16:35:06 +03:00
static inline void print_indented ( const String & str , int indent )
{
2021-02-17 18:18:53 +03:00
dbgln ( " {}{} " , String : : repeated ( ' ' , indent * 2 ) , str ) ;
2020-06-17 16:35:06 +03:00
}
2021-01-03 12:06:25 +03:00
static inline Optional < Position > merge_positions ( const Optional < Position > & left , const Optional < Position > & right )
{
if ( ! left . has_value ( ) )
return right ;
if ( ! right . has_value ( ) )
return left ;
return Position {
. start_offset = left - > start_offset ,
. end_offset = right - > end_offset ,
. start_line = left - > start_line ,
. end_line = right - > end_line ,
} ;
}
2020-06-22 14:07:20 +03:00
static inline Vector < Command > join_commands ( Vector < Command > left , Vector < Command > right )
{
Command command ;
auto last_in_left = left . take_last ( ) ;
auto first_in_right = right . take_first ( ) ;
2021-06-12 14:24:45 +03:00
command . argv . extend ( last_in_left . argv ) ;
command . argv . extend ( first_in_right . argv ) ;
2020-06-22 14:07:20 +03:00
2021-06-12 14:24:45 +03:00
command . redirections . extend ( last_in_left . redirections ) ;
command . redirections . extend ( first_in_right . redirections ) ;
2020-06-22 14:07:20 +03:00
command . should_wait = first_in_right . should_wait & & last_in_left . should_wait ;
command . is_pipe_source = first_in_right . is_pipe_source ;
2020-09-01 17:33:19 +03:00
command . should_notify_if_in_background = first_in_right . should_notify_if_in_background | | last_in_left . should_notify_if_in_background ;
2020-06-22 14:07:20 +03:00
2021-01-03 12:06:25 +03:00
command . position = merge_positions ( last_in_left . position , first_in_right . position ) ;
2020-06-22 14:07:20 +03:00
Vector < Command > commands ;
2021-06-12 14:24:45 +03:00
commands . extend ( left ) ;
2020-06-22 14:07:20 +03:00
commands . append ( command ) ;
2021-06-12 14:24:45 +03:00
commands . extend ( right ) ;
2020-06-22 14:07:20 +03:00
return commands ;
}
2021-03-13 02:40:18 +03:00
static String resolve_slices ( RefPtr < Shell > shell , String & & input_value , NonnullRefPtrVector < Slice > slices )
{
if ( slices . is_empty ( ) )
return move ( input_value ) ;
for ( auto & slice : slices ) {
auto value = slice . run ( shell ) ;
if ( ! value ) {
shell - > raise_error ( Shell : : ShellError : : InvalidSliceContentsError , " Invalid slice contents " , slice . position ( ) ) ;
return move ( input_value ) ;
}
auto index_values = value - > resolve_as_list ( shell ) ;
Vector < size_t > indices ;
indices . ensure_capacity ( index_values . size ( ) ) ;
size_t i = 0 ;
for ( auto & value : index_values ) {
auto maybe_index = value . to_int ( ) ;
if ( ! maybe_index . has_value ( ) ) {
shell - > raise_error ( Shell : : ShellError : : InvalidSliceContentsError , String : : formatted ( " Invalid value in slice index {}: {} (expected a number) " , i , value ) , slice . position ( ) ) ;
return move ( input_value ) ;
}
+ + i ;
auto index = maybe_index . value ( ) ;
auto original_index = index ;
if ( index < 0 )
index + = input_value . length ( ) ;
if ( index < 0 | | ( size_t ) index > = input_value . length ( ) ) {
shell - > raise_error ( Shell : : ShellError : : InvalidSliceContentsError , String : : formatted ( " Slice index {} (evaluated as {}) out of value bounds [0-{}) " , index , original_index , input_value . length ( ) ) , slice . position ( ) ) ;
return move ( input_value ) ;
}
indices . unchecked_append ( index ) ;
}
StringBuilder builder { indices . size ( ) } ;
for ( auto & index : indices )
builder . append ( input_value [ index ] ) ;
input_value = builder . build ( ) ;
}
return move ( input_value ) ;
}
static Vector < String > resolve_slices ( RefPtr < Shell > shell , Vector < String > & & values , NonnullRefPtrVector < Slice > slices )
{
if ( slices . is_empty ( ) )
return move ( values ) ;
for ( auto & slice : slices ) {
auto value = slice . run ( shell ) ;
if ( ! value ) {
shell - > raise_error ( Shell : : ShellError : : InvalidSliceContentsError , " Invalid slice contents " , slice . position ( ) ) ;
return move ( values ) ;
}
auto index_values = value - > resolve_as_list ( shell ) ;
Vector < size_t > indices ;
indices . ensure_capacity ( index_values . size ( ) ) ;
size_t i = 0 ;
for ( auto & value : index_values ) {
auto maybe_index = value . to_int ( ) ;
if ( ! maybe_index . has_value ( ) ) {
shell - > raise_error ( Shell : : ShellError : : InvalidSliceContentsError , String : : formatted ( " Invalid value in slice index {}: {} (expected a number) " , i , value ) , slice . position ( ) ) ;
return move ( values ) ;
}
+ + i ;
auto index = maybe_index . value ( ) ;
auto original_index = index ;
if ( index < 0 )
index + = values . size ( ) ;
if ( index < 0 | | ( size_t ) index > = values . size ( ) ) {
shell - > raise_error ( Shell : : ShellError : : InvalidSliceContentsError , String : : formatted ( " Slice index {} (evaluated as {}) out of value bounds [0-{}) " , index , original_index , values . size ( ) ) , slice . position ( ) ) ;
return move ( values ) ;
}
indices . unchecked_append ( index ) ;
}
Vector < String > result ;
result . ensure_capacity ( indices . size ( ) ) ;
for ( auto & index : indices )
result . unchecked_append ( values [ index ] ) ;
values = move ( result ) ;
}
return move ( values ) ;
}
2021-04-29 05:28:46 +03:00
void Node : : clear_syntax_error ( )
{
m_syntax_error_node - > clear_syntax_error ( ) ;
}
void Node : : set_is_syntax_error ( const SyntaxError & error_node )
{
if ( ! m_syntax_error_node ) {
m_syntax_error_node = error_node ;
} else {
m_syntax_error_node - > set_is_syntax_error ( error_node ) ;
}
}
bool Node : : is_syntax_error ( ) const
{
return m_syntax_error_node & & m_syntax_error_node - > is_syntax_error ( ) ;
}
2020-09-16 03:37:14 +03:00
void Node : : for_each_entry ( RefPtr < Shell > shell , Function < IterationDecision ( NonnullRefPtr < Value > ) > callback )
2020-08-21 02:18:23 +03:00
{
auto value = run ( shell ) - > resolve_without_cast ( shell ) ;
if ( value - > is_job ( ) ) {
callback ( value ) ;
return ;
}
2020-08-22 12:26:11 +03:00
if ( value - > is_list_without_resolution ( ) ) {
auto list = value - > resolve_without_cast ( shell ) ;
for ( auto & element : static_cast < ListValue * > ( list . ptr ( ) ) - > values ( ) ) {
if ( callback ( element ) = = IterationDecision : : Break )
break ;
}
return ;
}
2020-08-21 02:18:23 +03:00
auto list = value - > resolve_as_list ( shell ) ;
for ( auto & element : list ) {
if ( callback ( create < StringValue > ( move ( element ) ) ) = = IterationDecision : : Break )
break ;
}
}
2020-09-07 19:19:53 +03:00
Vector < Command > Node : : to_lazy_evaluated_commands ( RefPtr < Shell > shell )
{
if ( would_execute ( ) ) {
// Wrap the node in a "should immediately execute next" command.
return {
2021-01-03 12:06:25 +03:00
Command { { } , { } , true , false , true , true , { } , { NodeWithAction ( * this , NodeWithAction : : Sequence ) } , position ( ) }
2020-09-07 19:19:53 +03:00
} ;
}
return run ( shell ) - > resolve_as_commands ( shell ) ;
}
2020-06-17 16:35:06 +03:00
void Node : : dump ( int level ) const
{
2021-01-11 16:30:22 +03:00
print_indented ( String : : formatted ( " {} at {}:{} (from {}.{} to {}.{}) " ,
2020-09-28 13:57:20 +03:00
class_name ( ) . characters ( ) ,
m_position . start_offset ,
m_position . end_offset ,
m_position . start_line . line_number ,
m_position . start_line . line_column ,
m_position . end_line . line_number ,
m_position . end_line . line_column ) ,
level ) ;
2020-06-17 16:35:06 +03:00
}
Node : : Node ( Position position )
: m_position ( position )
{
}
2020-06-29 04:56:06 +03:00
Vector < Line : : CompletionSuggestion > Node : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
2020-06-17 16:35:06 +03:00
{
2020-06-29 04:56:06 +03:00
auto matching_node = hit_test_result . matching_node ;
2020-06-17 16:35:06 +03:00
if ( matching_node ) {
if ( matching_node - > is_bareword ( ) ) {
auto * node = static_cast < BarewordLiteral * > ( matching_node . ptr ( ) ) ;
2021-01-09 03:02:19 +03:00
auto corrected_offset = find_offset_into_node ( node - > text ( ) , offset - matching_node - > position ( ) . start_offset ) ;
2020-06-17 16:35:06 +03:00
if ( corrected_offset > node - > text ( ) . length ( ) )
return { } ;
2020-06-29 04:56:06 +03:00
auto & text = node - > text ( ) ;
// If the literal isn't an option, treat it as a path.
if ( ! ( text . starts_with ( " - " ) | | text = = " -- " | | text = = " - " ) )
2021-04-20 17:17:38 +03:00
return shell . complete_path ( " " , text , corrected_offset , Shell : : ExecutableOnly : : No ) ;
2020-06-29 04:56:06 +03:00
// If the literal is an option, we have to know the program name
// should we have no way to get that, bail early.
if ( ! hit_test_result . closest_command_node )
return { } ;
auto program_name_node = hit_test_result . closest_command_node - > leftmost_trivial_literal ( ) ;
if ( ! program_name_node )
return { } ;
String program_name ;
if ( program_name_node - > is_bareword ( ) )
program_name = static_cast < BarewordLiteral * > ( program_name_node . ptr ( ) ) - > text ( ) ;
else
program_name = static_cast < StringLiteral * > ( program_name_node . ptr ( ) ) - > text ( ) ;
return shell . complete_option ( program_name , text , corrected_offset ) ;
2020-06-17 16:35:06 +03:00
}
return { } ;
}
auto result = hit_test_position ( offset ) ;
if ( ! result . matching_node )
return { } ;
auto node = result . matching_node ;
if ( node - > is_bareword ( ) | | node ! = result . closest_node_with_semantic_meaning )
node = result . closest_node_with_semantic_meaning ;
if ( ! node )
return { } ;
2020-06-29 04:56:06 +03:00
return node - > complete_for_editor ( shell , offset , result ) ;
2020-06-17 16:35:06 +03:00
}
Vector < Line : : CompletionSuggestion > Node : : complete_for_editor ( Shell & shell , size_t offset )
{
2020-06-29 04:56:06 +03:00
return Node : : complete_for_editor ( shell , offset , { nullptr , nullptr , nullptr } ) ;
2020-06-17 16:35:06 +03:00
}
Node : : ~ Node ( )
{
}
void And : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_left - > dump ( level + 1 ) ;
m_right - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > And : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-09-07 19:19:53 +03:00
auto commands = m_left - > to_lazy_evaluated_commands ( shell ) ;
2020-09-01 07:12:16 +03:00
commands . last ( ) . next_chain . append ( NodeWithAction { * m_right , NodeWithAction : : And } ) ;
return create < CommandSequenceValue > ( move ( commands ) ) ;
2020-06-17 16:35:06 +03:00
}
void And : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
metadata . is_first_in_list = true ;
m_left - > highlight_in_editor ( editor , shell , metadata ) ;
m_right - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult And : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
auto result = m_left - > hit_test_position ( offset ) ;
2020-06-29 04:56:06 +03:00
if ( result . matching_node ) {
if ( ! result . closest_command_node )
result . closest_command_node = m_right ;
2020-06-17 16:35:06 +03:00
return result ;
2020-06-29 04:56:06 +03:00
}
2020-09-16 03:37:14 +03:00
2020-06-29 04:56:06 +03:00
result = m_right - > hit_test_position ( offset ) ;
if ( ! result . closest_command_node )
result . closest_command_node = m_right ;
return result ;
2020-06-17 16:35:06 +03:00
}
2020-09-28 13:57:20 +03:00
And : : And ( Position position , NonnullRefPtr < Node > left , NonnullRefPtr < Node > right , Position and_position )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_left ( move ( left ) )
, m_right ( move ( right ) )
2020-09-28 13:57:20 +03:00
, m_and_position ( and_position )
2020-06-17 16:35:06 +03:00
{
2020-06-28 17:12:57 +03:00
if ( m_left - > is_syntax_error ( ) )
set_is_syntax_error ( m_left - > syntax_error_node ( ) ) ;
else if ( m_right - > is_syntax_error ( ) )
set_is_syntax_error ( m_right - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
And : : ~ And ( )
{
}
void ListConcatenate : : dump ( int level ) const
{
Node : : dump ( level ) ;
2020-07-12 00:11:24 +03:00
for ( auto & element : m_list )
element - > dump ( level + 1 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > ListConcatenate : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-07-12 00:11:24 +03:00
RefPtr < Value > result = nullptr ;
2020-06-22 14:07:20 +03:00
2020-07-12 00:11:24 +03:00
for ( auto & element : m_list ) {
if ( ! result ) {
2020-08-07 10:36:15 +03:00
result = create < ListValue > ( { element - > run ( shell ) - > resolve_without_cast ( shell ) } ) ;
2020-07-12 00:11:24 +03:00
continue ;
}
auto element_value = element - > run ( shell ) - > resolve_without_cast ( shell ) ;
if ( result - > is_command ( ) | | element_value - > is_command ( ) ) {
auto joined_commands = join_commands ( result - > resolve_as_commands ( shell ) , element_value - > resolve_as_commands ( shell ) ) ;
2021-01-03 12:06:25 +03:00
if ( joined_commands . size ( ) = = 1 ) {
auto & command = joined_commands [ 0 ] ;
command . position = position ( ) ;
result = create < CommandValue > ( command ) ;
} else {
2020-07-12 00:11:24 +03:00
result = create < CommandSequenceValue > ( move ( joined_commands ) ) ;
2021-01-03 12:06:25 +03:00
}
2020-07-12 00:11:24 +03:00
} else {
2020-08-07 10:33:05 +03:00
NonnullRefPtrVector < Value > values ;
2020-06-22 14:07:20 +03:00
2020-07-12 00:11:24 +03:00
if ( result - > is_list_without_resolution ( ) ) {
2021-06-12 14:24:45 +03:00
values . extend ( static_cast < ListValue * > ( result . ptr ( ) ) - > values ( ) ) ;
2020-07-12 00:11:24 +03:00
} else {
for ( auto & result : result - > resolve_as_list ( shell ) )
values . append ( create < StringValue > ( result ) ) ;
}
2020-08-07 10:36:15 +03:00
values . append ( element_value ) ;
2020-07-12 00:11:24 +03:00
result = create < ListValue > ( move ( values ) ) ;
}
2020-06-22 14:07:20 +03:00
}
2020-07-12 00:11:24 +03:00
if ( ! result )
return create < ListValue > ( { } ) ;
2020-06-17 16:35:06 +03:00
2020-07-12 00:11:24 +03:00
return result ;
2020-06-17 16:35:06 +03:00
}
2021-01-18 04:06:19 +03:00
void ListConcatenate : : for_each_entry ( RefPtr < Shell > shell , Function < IterationDecision ( NonnullRefPtr < Value > ) > callback )
{
for ( auto & entry : m_list ) {
auto value = entry - > run ( shell ) ;
if ( ! value )
continue ;
if ( callback ( value . release_nonnull ( ) ) = = IterationDecision : : Break )
break ;
}
}
2020-06-17 16:35:06 +03:00
void ListConcatenate : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
auto first = metadata . is_first_in_list ;
metadata . is_first_in_list = false ;
2020-07-12 00:11:24 +03:00
2020-06-17 16:35:06 +03:00
metadata . is_first_in_list = first ;
2020-07-12 00:11:24 +03:00
for ( auto & element : m_list ) {
element - > highlight_in_editor ( editor , shell , metadata ) ;
metadata . is_first_in_list = false ;
}
2020-06-17 16:35:06 +03:00
}
2021-03-02 09:05:04 +03:00
HitTestResult ListConcatenate : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
2020-07-12 00:11:24 +03:00
bool first = true ;
for ( auto & element : m_list ) {
auto result = element - > hit_test_position ( offset ) ;
if ( ! result . closest_node_with_semantic_meaning & & ! first )
result . closest_node_with_semantic_meaning = this ;
if ( result . matching_node )
return result ;
first = false ;
}
return { } ;
2020-06-17 16:35:06 +03:00
}
2020-06-29 04:56:06 +03:00
RefPtr < Node > ListConcatenate : : leftmost_trivial_literal ( ) const
{
2020-07-12 00:11:24 +03:00
if ( m_list . is_empty ( ) )
return nullptr ;
return m_list . first ( ) - > leftmost_trivial_literal ( ) ;
2020-06-29 04:56:06 +03:00
}
2020-09-16 03:37:14 +03:00
ListConcatenate : : ListConcatenate ( Position position , Vector < NonnullRefPtr < Node > > list )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_list ( move ( list ) )
{
2020-07-12 00:11:24 +03:00
for ( auto & element : m_list ) {
if ( element - > is_syntax_error ( ) ) {
set_is_syntax_error ( element - > syntax_error_node ( ) ) ;
break ;
}
}
2020-06-17 16:35:06 +03:00
}
ListConcatenate : : ~ ListConcatenate ( )
{
}
void Background : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_command - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Background : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-09-07 19:19:53 +03:00
auto commands = m_command - > to_lazy_evaluated_commands ( shell ) ;
2020-09-01 07:12:16 +03:00
for ( auto & command : commands )
command . should_wait = false ;
2020-06-17 16:35:06 +03:00
2020-06-20 16:30:45 +03:00
return create < CommandSequenceValue > ( move ( commands ) ) ;
2020-06-17 16:35:06 +03:00
}
void Background : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
m_command - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult Background : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
return m_command - > hit_test_position ( offset ) ;
}
2020-09-16 03:37:14 +03:00
Background : : Background ( Position position , NonnullRefPtr < Node > command )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_command ( move ( command ) )
{
2020-06-23 17:40:41 +03:00
if ( m_command - > is_syntax_error ( ) )
2020-06-28 17:12:57 +03:00
set_is_syntax_error ( m_command - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
Background : : ~ Background ( )
{
}
void BarewordLiteral : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( m_text , level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > BarewordLiteral : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
2020-06-20 16:30:45 +03:00
return create < StringValue > ( m_text ) ;
2020-06-17 16:35:06 +03:00
}
void BarewordLiteral : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
if ( metadata . is_first_in_list ) {
2020-08-03 12:06:42 +03:00
if ( shell . is_runnable ( m_text ) ) {
2020-08-03 03:04:27 +03:00
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Bold } ) ;
2020-08-03 12:06:42 +03:00
} else {
2020-08-03 03:04:27 +03:00
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Red ) } ) ;
}
2020-06-17 16:35:06 +03:00
return ;
}
2020-08-03 03:04:27 +03:00
2020-06-17 16:35:06 +03:00
if ( m_text . starts_with ( ' - ' ) ) {
if ( m_text = = " -- " ) {
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Green ) } ) ;
return ;
}
if ( m_text = = " - " )
return ;
if ( m_text . starts_with ( " -- " ) ) {
2021-05-24 12:50:46 +03:00
auto index = m_text . find ( ' = ' ) . value_or ( m_text . length ( ) - 1 ) + 1 ;
2020-06-17 16:35:06 +03:00
editor . stylize ( { m_position . start_offset , m_position . start_offset + index } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Cyan ) } ) ;
} else {
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Cyan ) } ) ;
}
}
if ( Core : : File : : exists ( m_text ) ) {
auto realpath = shell . resolve_path ( m_text ) ;
auto url = URL : : create_with_file_protocol ( realpath ) ;
url . set_host ( shell . hostname ) ;
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Hyperlink ( url . to_string ( ) ) } ) ;
}
}
BarewordLiteral : : BarewordLiteral ( Position position , String text )
: Node ( move ( position ) )
, m_text ( move ( text ) )
{
}
BarewordLiteral : : ~ BarewordLiteral ( )
{
}
2020-10-24 17:43:02 +03:00
void BraceExpansion : : dump ( int level ) const
{
Node : : dump ( level ) ;
for ( auto & entry : m_entries )
entry . dump ( level + 1 ) ;
}
RefPtr < Value > BraceExpansion : : run ( RefPtr < Shell > shell )
{
NonnullRefPtrVector < Value > values ;
for ( auto & entry : m_entries ) {
auto value = entry . run ( shell ) ;
if ( value )
values . append ( value . release_nonnull ( ) ) ;
}
return create < ListValue > ( move ( values ) ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult BraceExpansion : : hit_test_position ( size_t offset ) const
2020-10-24 17:43:02 +03:00
{
for ( auto & entry : m_entries ) {
auto result = entry . hit_test_position ( offset ) ;
if ( result . matching_node ) {
if ( ! result . closest_command_node )
result . closest_command_node = & entry ;
return result ;
}
}
return { } ;
}
void BraceExpansion : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
for ( auto & entry : m_entries ) {
entry . highlight_in_editor ( editor , shell , metadata ) ;
metadata . is_first_in_list = false ;
}
}
BraceExpansion : : BraceExpansion ( Position position , NonnullRefPtrVector < Node > entries )
: Node ( move ( position ) )
, m_entries ( move ( entries ) )
{
for ( auto & entry : m_entries ) {
if ( entry . is_syntax_error ( ) ) {
set_is_syntax_error ( entry . syntax_error_node ( ) ) ;
break ;
}
}
}
BraceExpansion : : ~ BraceExpansion ( )
{
}
2020-06-17 16:35:06 +03:00
void CastToCommand : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_inner - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > CastToCommand : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
if ( m_inner - > is_command ( ) )
2020-06-23 17:40:41 +03:00
return m_inner - > run ( shell ) ;
2020-06-17 16:35:06 +03:00
2020-06-23 17:40:41 +03:00
auto value = m_inner - > run ( shell ) - > resolve_without_cast ( shell ) ;
2020-06-22 14:07:20 +03:00
if ( value - > is_command ( ) )
return value ;
2020-06-17 16:35:06 +03:00
2020-06-23 17:40:41 +03:00
auto argv = value - > resolve_as_list ( shell ) ;
2021-01-03 12:06:25 +03:00
return create < CommandValue > ( move ( argv ) , position ( ) ) ;
2020-06-17 16:35:06 +03:00
}
void CastToCommand : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
m_inner - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult CastToCommand : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
auto result = m_inner - > hit_test_position ( offset ) ;
if ( ! result . closest_node_with_semantic_meaning )
result . closest_node_with_semantic_meaning = this ;
return result ;
}
2020-06-29 04:56:06 +03:00
Vector < Line : : CompletionSuggestion > CastToCommand : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
2020-06-17 16:35:06 +03:00
{
2020-06-29 04:56:06 +03:00
auto matching_node = hit_test_result . matching_node ;
2020-07-25 08:59:30 +03:00
if ( ! matching_node | | ! matching_node - > is_bareword ( ) )
2020-06-17 16:35:06 +03:00
return { } ;
auto corrected_offset = offset - matching_node - > position ( ) . start_offset ;
auto * node = static_cast < BarewordLiteral * > ( matching_node . ptr ( ) ) ;
if ( corrected_offset > node - > text ( ) . length ( ) )
return { } ;
return shell . complete_program_name ( node - > text ( ) , corrected_offset ) ;
}
2020-06-29 04:56:06 +03:00
RefPtr < Node > CastToCommand : : leftmost_trivial_literal ( ) const
{
return m_inner - > leftmost_trivial_literal ( ) ;
}
2020-09-16 03:37:14 +03:00
CastToCommand : : CastToCommand ( Position position , NonnullRefPtr < Node > inner )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_inner ( move ( inner ) )
{
2020-06-23 17:40:41 +03:00
if ( m_inner - > is_syntax_error ( ) )
2020-06-28 17:12:57 +03:00
set_is_syntax_error ( m_inner - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
CastToCommand : : ~ CastToCommand ( )
{
}
void CastToList : : dump ( int level ) const
{
Node : : dump ( level ) ;
2020-06-22 14:07:20 +03:00
if ( m_inner )
m_inner - > dump ( level + 1 ) ;
else
print_indented ( " (empty) " , level + 1 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > CastToList : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-06-22 14:07:20 +03:00
if ( ! m_inner )
return create < ListValue > ( { } ) ;
2020-07-12 00:11:24 +03:00
auto inner_value = m_inner - > run ( shell ) - > resolve_without_cast ( shell ) ;
2020-06-22 14:07:20 +03:00
2020-07-12 00:11:24 +03:00
if ( inner_value - > is_command ( ) | | inner_value - > is_list ( ) )
2020-06-22 14:07:20 +03:00
return inner_value ;
2020-06-23 17:40:41 +03:00
auto values = inner_value - > resolve_as_list ( shell ) ;
2020-08-07 10:33:05 +03:00
NonnullRefPtrVector < Value > cast_values ;
2020-06-17 16:35:06 +03:00
for ( auto & value : values )
2020-06-20 16:30:45 +03:00
cast_values . append ( create < StringValue > ( value ) ) ;
2020-06-17 16:35:06 +03:00
2020-06-20 16:30:45 +03:00
return create < ListValue > ( cast_values ) ;
2020-06-17 16:35:06 +03:00
}
2021-01-18 04:06:19 +03:00
void CastToList : : for_each_entry ( RefPtr < Shell > shell , Function < IterationDecision ( NonnullRefPtr < Value > ) > callback )
{
if ( m_inner )
m_inner - > for_each_entry ( shell , move ( callback ) ) ;
}
2020-06-17 16:35:06 +03:00
void CastToList : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
2020-06-22 14:07:20 +03:00
if ( m_inner )
m_inner - > highlight_in_editor ( editor , shell , metadata ) ;
2020-06-17 16:35:06 +03:00
}
2021-03-02 09:05:04 +03:00
HitTestResult CastToList : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
2020-06-22 14:07:20 +03:00
if ( ! m_inner )
return { } ;
2020-06-17 16:35:06 +03:00
return m_inner - > hit_test_position ( offset ) ;
}
2020-06-29 04:56:06 +03:00
RefPtr < Node > CastToList : : leftmost_trivial_literal ( ) const
{
return m_inner - > leftmost_trivial_literal ( ) ;
}
2020-06-17 16:35:06 +03:00
CastToList : : CastToList ( Position position , RefPtr < Node > inner )
: Node ( move ( position ) )
, m_inner ( move ( inner ) )
{
2020-06-23 17:40:41 +03:00
if ( m_inner & & m_inner - > is_syntax_error ( ) )
2020-06-28 17:12:57 +03:00
set_is_syntax_error ( m_inner - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
CastToList : : ~ CastToList ( )
{
}
void CloseFdRedirection : : dump ( int level ) const
{
Node : : dump ( level ) ;
2021-04-21 23:16:14 +03:00
print_indented ( String : : formatted ( " {} -> Close " , m_fd ) , level ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > CloseFdRedirection : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
Command command ;
2021-01-03 12:06:25 +03:00
command . position = position ( ) ;
2021-04-23 17:46:57 +03:00
command . redirections . append ( adopt_ref ( * new CloseRedirection ( m_fd ) ) ) ;
2020-06-20 16:30:45 +03:00
return create < CommandValue > ( move ( command ) ) ;
2020-06-17 16:35:06 +03:00
}
void CloseFdRedirection : : highlight_in_editor ( Line : : Editor & editor , Shell & , HighlightMetadata )
{
editor . stylize ( { m_position . start_offset , m_position . end_offset - 1 } , { Line : : Style : : Foreground ( 0x87 , 0x9b , 0xcd ) } ) ; // 25% Darkened Periwinkle
editor . stylize ( { m_position . end_offset - 1 , m_position . end_offset } , { Line : : Style : : Foreground ( 0xff , 0x7e , 0x00 ) } ) ; // Amber
}
CloseFdRedirection : : CloseFdRedirection ( Position position , int fd )
: Node ( move ( position ) )
, m_fd ( fd )
{
}
CloseFdRedirection : : ~ CloseFdRedirection ( )
{
}
void CommandLiteral : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( " (Generated command literal) " , level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > CommandLiteral : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
2020-06-20 16:30:45 +03:00
return create < CommandValue > ( m_command ) ;
2020-06-17 16:35:06 +03:00
}
CommandLiteral : : CommandLiteral ( Position position , Command command )
: Node ( move ( position ) )
, m_command ( move ( command ) )
{
}
CommandLiteral : : ~ CommandLiteral ( )
{
}
void Comment : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( m_text , level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Comment : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
2020-07-05 18:18:26 +03:00
return create < ListValue > ( { } ) ;
2020-06-17 16:35:06 +03:00
}
void Comment : : highlight_in_editor ( Line : : Editor & editor , Shell & , HighlightMetadata )
{
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( 150 , 150 , 150 ) } ) ; // Light gray
}
Comment : : Comment ( Position position , String text )
: Node ( move ( position ) )
, m_text ( move ( text ) )
{
}
Comment : : ~ Comment ( )
{
}
2020-12-10 17:55:13 +03:00
void ContinuationControl : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( m_kind = = Continue ? " (Continue) " : " (Break) " , level + 1 ) ;
}
RefPtr < Value > ContinuationControl : : run ( RefPtr < Shell > shell )
{
if ( m_kind = = Break )
2021-01-03 12:08:20 +03:00
shell - > raise_error ( Shell : : ShellError : : InternalControlFlowBreak , { } , position ( ) ) ;
2020-12-10 17:55:13 +03:00
else if ( m_kind = = Continue )
2021-01-03 12:08:20 +03:00
shell - > raise_error ( Shell : : ShellError : : InternalControlFlowContinue , { } , position ( ) ) ;
2020-12-10 17:55:13 +03:00
else
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-12-10 17:55:13 +03:00
return create < ListValue > ( { } ) ;
}
void ContinuationControl : : highlight_in_editor ( Line : : Editor & editor , Shell & , HighlightMetadata )
{
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
}
2020-06-17 16:35:06 +03:00
void DoubleQuotedString : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_inner - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > DoubleQuotedString : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
StringBuilder builder ;
2020-06-23 17:40:41 +03:00
auto values = m_inner - > run ( shell ) - > resolve_as_list ( shell ) ;
2020-06-17 16:35:06 +03:00
builder . join ( " " , values ) ;
2020-06-20 16:30:45 +03:00
return create < StringValue > ( builder . to_string ( ) ) ;
2020-06-17 16:35:06 +03:00
}
void DoubleQuotedString : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
Line : : Style style { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ;
if ( metadata . is_first_in_list )
style . unify_with ( { Line : : Style : : Bold } ) ;
editor . stylize ( { m_position . start_offset , m_position . end_offset } , style ) ;
metadata . is_first_in_list = false ;
m_inner - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult DoubleQuotedString : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
return m_inner - > hit_test_position ( offset ) ;
}
DoubleQuotedString : : DoubleQuotedString ( Position position , RefPtr < Node > inner )
: Node ( move ( position ) )
, m_inner ( move ( inner ) )
{
2020-06-23 17:40:41 +03:00
if ( m_inner - > is_syntax_error ( ) )
2020-06-28 17:12:57 +03:00
set_is_syntax_error ( m_inner - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
DoubleQuotedString : : ~ DoubleQuotedString ( )
{
}
void DynamicEvaluate : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_inner - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > DynamicEvaluate : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-06-23 17:40:41 +03:00
auto result = m_inner - > run ( shell ) - > resolve_without_cast ( shell ) ;
2020-06-17 16:35:06 +03:00
// Dynamic Evaluation behaves differently between strings and lists.
// Strings are treated as variables, and Lists are treated as commands.
if ( result - > is_string ( ) ) {
2020-06-23 17:40:41 +03:00
auto name_part = result - > resolve_as_list ( shell ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( name_part . size ( ) = = 1 ) ;
2020-06-20 16:30:45 +03:00
return create < SimpleVariableValue > ( name_part [ 0 ] ) ;
2020-06-17 16:35:06 +03:00
}
// If it's anything else, we're just gonna cast it to a list.
2020-06-23 17:40:41 +03:00
auto list = result - > resolve_as_list ( shell ) ;
2021-01-03 12:06:25 +03:00
return create < CommandValue > ( move ( list ) , position ( ) ) ;
2020-06-17 16:35:06 +03:00
}
void DynamicEvaluate : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
m_inner - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult DynamicEvaluate : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
return m_inner - > hit_test_position ( offset ) ;
}
2020-09-16 03:37:14 +03:00
DynamicEvaluate : : DynamicEvaluate ( Position position , NonnullRefPtr < Node > inner )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_inner ( move ( inner ) )
{
2020-06-23 17:40:41 +03:00
if ( m_inner - > is_syntax_error ( ) )
2020-06-28 17:12:57 +03:00
set_is_syntax_error ( m_inner - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
DynamicEvaluate : : ~ DynamicEvaluate ( )
{
}
void Fd2FdRedirection : : dump ( int level ) const
{
Node : : dump ( level ) ;
2021-04-21 23:16:14 +03:00
print_indented ( String : : formatted ( " {} -> {} " , m_old_fd , m_new_fd ) , level ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Fd2FdRedirection : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
Command command ;
2021-01-03 12:06:25 +03:00
command . position = position ( ) ;
2020-10-27 21:08:37 +03:00
command . redirections . append ( FdRedirection : : create ( m_new_fd , m_old_fd , Rewiring : : Close : : None ) ) ;
2020-06-20 16:30:45 +03:00
return create < CommandValue > ( move ( command ) ) ;
2020-06-17 16:35:06 +03:00
}
void Fd2FdRedirection : : highlight_in_editor ( Line : : Editor & editor , Shell & , HighlightMetadata )
{
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( 0x87 , 0x9b , 0xcd ) } ) ; // 25% Darkened Periwinkle
}
Fd2FdRedirection : : Fd2FdRedirection ( Position position , int src , int dst )
: Node ( move ( position ) )
2020-10-27 21:08:37 +03:00
, m_old_fd ( src )
, m_new_fd ( dst )
2020-06-17 16:35:06 +03:00
{
}
Fd2FdRedirection : : ~ Fd2FdRedirection ( )
{
}
2020-09-13 14:24:33 +03:00
void FunctionDeclaration : : dump ( int level ) const
{
Node : : dump ( level ) ;
2021-04-21 23:16:14 +03:00
print_indented ( String : : formatted ( " (name: {}) \n " , m_name . name ) , level + 1 ) ;
print_indented ( " (argument names) " , level + 1 ) ;
2020-09-13 14:24:33 +03:00
for ( auto & arg : m_arguments )
2021-04-21 23:16:14 +03:00
print_indented ( String : : formatted ( " (name: {}) \n " , arg . name ) , level + 2 ) ;
2020-09-13 14:24:33 +03:00
print_indented ( " (body) " , level + 1 ) ;
if ( m_block )
m_block - > dump ( level + 2 ) ;
else
print_indented ( " (null) " , level + 2 ) ;
}
RefPtr < Value > FunctionDeclaration : : run ( RefPtr < Shell > shell )
{
Vector < String > args ;
for ( auto & arg : m_arguments )
args . append ( arg . name ) ;
shell - > define_function ( m_name . name , move ( args ) , m_block ) ;
return create < ListValue > ( { } ) ;
}
void FunctionDeclaration : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
editor . stylize ( { m_name . position . start_offset , m_name . position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Blue ) } ) ;
for ( auto & arg : m_arguments )
editor . stylize ( { arg . position . start_offset , arg . position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Blue ) , Line : : Style : : Italic } ) ;
metadata . is_first_in_list = true ;
if ( m_block )
m_block - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult FunctionDeclaration : : hit_test_position ( size_t offset ) const
2020-09-13 14:24:33 +03:00
{
2020-09-16 03:37:14 +03:00
if ( ! m_block )
return { } ;
2020-09-14 12:29:06 +03:00
auto result = m_block - > hit_test_position ( offset ) ;
if ( result . matching_node & & result . matching_node - > is_simple_variable ( ) )
result . closest_node_with_semantic_meaning = this ;
return result ;
}
Vector < Line : : CompletionSuggestion > FunctionDeclaration : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
{
auto matching_node = hit_test_result . matching_node ;
if ( ! matching_node )
return { } ;
if ( ! matching_node - > is_simple_variable ( ) )
return matching_node - > complete_for_editor ( shell , offset , hit_test_result ) ;
auto corrected_offset = offset - matching_node - > position ( ) . start_offset - 1 ; // Skip the first '$'
auto * node = static_cast < SimpleVariable * > ( matching_node . ptr ( ) ) ;
auto name = node - > name ( ) . substring_view ( 0 , corrected_offset ) ;
Vector < Line : : CompletionSuggestion > results ;
for ( auto & arg : m_arguments ) {
if ( arg . name . starts_with ( name ) )
results . append ( arg . name ) ;
}
2021-06-12 14:24:45 +03:00
results . extend ( matching_node - > complete_for_editor ( shell , offset , hit_test_result ) ) ;
2020-09-14 12:29:06 +03:00
return results ;
2020-09-13 14:24:33 +03:00
}
FunctionDeclaration : : FunctionDeclaration ( Position position , NameWithPosition name , Vector < NameWithPosition > arguments , RefPtr < AST : : Node > body )
: Node ( move ( position ) )
, m_name ( move ( name ) )
, m_arguments ( arguments )
, m_block ( move ( body ) )
{
if ( m_block & & m_block - > is_syntax_error ( ) )
set_is_syntax_error ( m_block - > syntax_error_node ( ) ) ;
}
FunctionDeclaration : : ~ FunctionDeclaration ( )
{
}
2020-07-12 00:12:46 +03:00
void ForLoop : : dump ( int level ) const
{
Node : : dump ( level ) ;
2021-03-05 17:55:09 +03:00
if ( m_variable . has_value ( ) )
print_indented ( String : : formatted ( " iterating with {} in " , m_variable - > name ) , level + 1 ) ;
if ( m_index_variable . has_value ( ) )
print_indented ( String : : formatted ( " with index name {} in " , m_index_variable - > name ) , level + 1 ) ;
2020-12-10 17:55:13 +03:00
if ( m_iterated_expression )
m_iterated_expression - > dump ( level + 2 ) ;
else
print_indented ( " (ever) " , level + 2 ) ;
2020-07-12 00:12:46 +03:00
print_indented ( " Running " , level + 1 ) ;
if ( m_block )
m_block - > dump ( level + 2 ) ;
else
print_indented ( " (null) " , level + 2 ) ;
}
RefPtr < Value > ForLoop : : run ( RefPtr < Shell > shell )
{
if ( ! m_block )
return create < ListValue > ( { } ) ;
2020-08-08 12:18:07 +03:00
size_t consecutive_interruptions = 0 ;
2020-12-10 17:55:13 +03:00
auto run = [ & ] ( auto & block_value ) {
if ( shell - > has_error ( Shell : : ShellError : : InternalControlFlowBreak ) ) {
shell - > take_error ( ) ;
2020-08-22 12:18:39 +03:00
return IterationDecision : : Break ;
2020-12-10 17:55:13 +03:00
}
2020-08-08 12:18:07 +03:00
2020-12-10 17:55:13 +03:00
if ( shell - > has_error ( Shell : : ShellError : : InternalControlFlowContinue ) ) {
shell - > take_error ( ) ;
return IterationDecision : : Continue ;
2020-08-22 12:18:39 +03:00
}
2020-07-12 00:12:46 +03:00
2021-01-18 04:06:19 +03:00
if ( ! shell - > has_error ( Shell : : ShellError : : None ) )
return IterationDecision : : Break ;
2020-07-12 00:12:46 +03:00
if ( block_value - > is_job ( ) ) {
auto job = static_cast < JobValue * > ( block_value . ptr ( ) ) - > job ( ) ;
if ( ! job | | job - > is_running_in_background ( ) )
2020-08-22 12:18:39 +03:00
return IterationDecision : : Continue ;
2020-07-12 00:12:46 +03:00
shell - > block_on_job ( job ) ;
2020-08-10 21:28:21 +03:00
if ( job - > signaled ( ) ) {
if ( job - > termination_signal ( ) = = SIGINT )
+ + consecutive_interruptions ;
else
2020-08-22 12:18:39 +03:00
return IterationDecision : : Break ;
2020-08-10 21:28:21 +03:00
} else {
2020-08-08 12:18:07 +03:00
consecutive_interruptions = 0 ;
2020-08-10 21:28:21 +03:00
}
2020-07-12 00:12:46 +03:00
}
2020-08-22 12:18:39 +03:00
return IterationDecision : : Continue ;
2020-12-10 17:55:13 +03:00
} ;
if ( m_iterated_expression ) {
2021-03-05 17:55:09 +03:00
auto variable_name = m_variable . has_value ( ) ? m_variable - > name : " it " ;
Optional < StringView > index_name = m_index_variable . has_value ( ) ? Optional < StringView > ( m_index_variable - > name ) : Optional < StringView > ( ) ;
size_t i = 0 ;
2020-12-10 17:55:13 +03:00
m_iterated_expression - > for_each_entry ( shell , [ & ] ( auto value ) {
if ( consecutive_interruptions = = 2 )
return IterationDecision : : Break ;
RefPtr < Value > block_value ;
{
auto frame = shell - > push_frame ( String : : formatted ( " for ({}) " , this ) ) ;
2021-03-05 17:55:09 +03:00
shell - > set_local_variable ( variable_name , value , true ) ;
if ( index_name . has_value ( ) )
shell - > set_local_variable ( index_name . value ( ) , create < AST : : StringValue > ( String : : number ( i ) ) , true ) ;
+ + i ;
2020-12-10 17:55:13 +03:00
block_value = m_block - > run ( shell ) ;
}
2021-03-05 17:55:09 +03:00
2020-12-10 17:55:13 +03:00
return run ( block_value ) ;
} ) ;
} else {
for ( ; ; ) {
if ( consecutive_interruptions = = 2 )
break ;
RefPtr < Value > block_value = m_block - > run ( shell ) ;
if ( run ( block_value ) = = IterationDecision : : Break )
break ;
}
}
2020-07-12 00:12:46 +03:00
return create < ListValue > ( { } ) ;
}
void ForLoop : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
2020-12-10 17:55:13 +03:00
auto is_loop = m_iterated_expression . is_null ( ) ;
editor . stylize ( { m_position . start_offset , m_position . start_offset + ( is_loop ? 4 : 3 ) } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
if ( ! is_loop ) {
if ( m_in_kw_position . has_value ( ) )
editor . stylize ( { m_in_kw_position . value ( ) . start_offset , m_in_kw_position . value ( ) . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
2020-07-12 00:12:46 +03:00
2021-03-05 17:55:09 +03:00
if ( m_index_kw_position . has_value ( ) )
editor . stylize ( { m_index_kw_position . value ( ) . start_offset , m_index_kw_position . value ( ) . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
2020-12-10 17:55:13 +03:00
metadata . is_first_in_list = false ;
m_iterated_expression - > highlight_in_editor ( editor , shell , metadata ) ;
}
2020-07-12 00:12:46 +03:00
2021-03-05 17:55:09 +03:00
if ( m_index_variable . has_value ( ) )
editor . stylize ( { m_index_variable - > position . start_offset , m_index_variable - > position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Blue ) , Line : : Style : : Italic } ) ;
if ( m_variable . has_value ( ) )
editor . stylize ( { m_variable - > position . start_offset , m_variable - > position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Blue ) , Line : : Style : : Italic } ) ;
2020-07-12 00:12:46 +03:00
metadata . is_first_in_list = true ;
if ( m_block )
m_block - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult ForLoop : : hit_test_position ( size_t offset ) const
2020-07-12 00:12:46 +03:00
{
2020-12-10 17:55:13 +03:00
if ( m_iterated_expression ) {
if ( auto result = m_iterated_expression - > hit_test_position ( offset ) ; result . matching_node )
return result ;
}
2020-07-12 00:12:46 +03:00
2020-09-16 03:37:14 +03:00
if ( ! m_block )
return { } ;
2020-07-12 00:12:46 +03:00
return m_block - > hit_test_position ( offset ) ;
}
2021-03-05 17:55:09 +03:00
ForLoop : : ForLoop ( Position position , Optional < NameWithPosition > variable , Optional < NameWithPosition > index_variable , RefPtr < AST : : Node > iterated_expr , RefPtr < AST : : Node > block , Optional < Position > in_kw_position , Optional < Position > index_kw_position )
2020-07-12 00:12:46 +03:00
: Node ( move ( position ) )
2021-03-05 17:55:09 +03:00
, m_variable ( move ( variable ) )
, m_index_variable ( move ( index_variable ) )
2020-07-12 00:12:46 +03:00
, m_iterated_expression ( move ( iterated_expr ) )
, m_block ( move ( block ) )
, m_in_kw_position ( move ( in_kw_position ) )
2021-03-05 17:55:09 +03:00
, m_index_kw_position ( move ( index_kw_position ) )
2020-07-12 00:12:46 +03:00
{
2020-12-10 17:55:13 +03:00
if ( m_iterated_expression & & m_iterated_expression - > is_syntax_error ( ) )
2020-07-12 00:12:46 +03:00
set_is_syntax_error ( m_iterated_expression - > syntax_error_node ( ) ) ;
else if ( m_block & & m_block - > is_syntax_error ( ) )
set_is_syntax_error ( m_block - > syntax_error_node ( ) ) ;
}
ForLoop : : ~ ForLoop ( )
{
}
2020-06-17 16:35:06 +03:00
void Glob : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( m_text , level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Glob : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
2021-01-03 12:06:25 +03:00
return create < GlobValue > ( m_text , position ( ) ) ;
2020-06-17 16:35:06 +03:00
}
void Glob : : highlight_in_editor ( Line : : Editor & editor , Shell & , HighlightMetadata metadata )
{
Line : : Style style { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Cyan ) } ;
if ( metadata . is_first_in_list )
style . unify_with ( { Line : : Style : : Bold } ) ;
editor . stylize ( { m_position . start_offset , m_position . end_offset } , move ( style ) ) ;
}
Glob : : Glob ( Position position , String text )
: Node ( move ( position ) )
, m_text ( move ( text ) )
{
}
Glob : : ~ Glob ( )
{
}
2021-04-29 05:34:00 +03:00
void Heredoc : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( " (End Key) " , level + 1 ) ;
print_indented ( m_end , level + 2 ) ;
print_indented ( " (Allows Interpolation) " , level + 1 ) ;
print_indented ( String : : formatted ( " {} " , m_allows_interpolation ) , level + 2 ) ;
print_indented ( " (Contents) " , level + 1 ) ;
if ( m_contents )
m_contents - > dump ( level + 2 ) ;
else
print_indented ( " (null) " , level + 2 ) ;
}
RefPtr < Value > Heredoc : : run ( RefPtr < Shell > shell )
{
if ( ! m_deindent )
return m_contents - > run ( shell ) ;
// To deindent, first split to lines...
auto value = m_contents - > run ( shell ) ;
if ( ! value )
return value ;
auto list = value - > resolve_as_list ( shell ) ;
// The list better have one entry, otherwise we've put the wrong kind of node inside this heredoc
VERIFY ( list . size ( ) = = 1 ) ;
auto lines = list . first ( ) . split_view ( ' \n ' ) ;
// Now just trim each line and put them back in a string
StringBuilder builder { list . first ( ) . length ( ) } ;
for ( auto & line : lines ) {
builder . append ( line . trim_whitespace ( TrimMode : : Left ) ) ;
builder . append ( ' \n ' ) ;
}
return create < StringValue > ( builder . to_string ( ) ) ;
}
void Heredoc : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
Line : : Style content_style { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ;
if ( metadata . is_first_in_list )
content_style . unify_with ( { Line : : Style : : Bold } ) ;
if ( ! m_contents )
content_style . unify_with ( { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Red ) } , true ) ;
editor . stylize ( { m_position . start_offset , m_position . end_offset } , content_style ) ;
if ( m_contents )
m_contents - > highlight_in_editor ( editor , shell , metadata ) ;
}
HitTestResult Heredoc : : hit_test_position ( size_t offset ) const
{
if ( ! m_contents )
return { } ;
return m_contents - > hit_test_position ( offset ) ;
}
Heredoc : : Heredoc ( Position position , String end , bool allow_interpolation , bool deindent )
: Node ( move ( position ) )
, m_end ( move ( end ) )
, m_allows_interpolation ( allow_interpolation )
, m_deindent ( deindent )
{
}
Heredoc : : ~ Heredoc ( )
{
}
2021-01-11 12:34:59 +03:00
void HistoryEvent : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( " Event Selector " , level + 1 ) ;
switch ( m_selector . event . kind ) {
case HistorySelector : : EventKind : : IndexFromStart :
print_indented ( " IndexFromStart " , level + 2 ) ;
break ;
case HistorySelector : : EventKind : : IndexFromEnd :
print_indented ( " IndexFromEnd " , level + 2 ) ;
break ;
case HistorySelector : : EventKind : : ContainingStringLookup :
print_indented ( " ContainingStringLookup " , level + 2 ) ;
break ;
case HistorySelector : : EventKind : : StartingStringLookup :
print_indented ( " StartingStringLookup " , level + 2 ) ;
break ;
}
print_indented ( String : : formatted ( " {}({}) " , m_selector . event . index , m_selector . event . text ) , level + 3 ) ;
print_indented ( " Word Selector " , level + 1 ) ;
auto print_word_selector = [ & ] ( const HistorySelector : : WordSelector & selector ) {
switch ( selector . kind ) {
case HistorySelector : : WordSelectorKind : : Index :
print_indented ( String : : formatted ( " Index {} " , selector . selector ) , level + 3 ) ;
break ;
case HistorySelector : : WordSelectorKind : : Last :
print_indented ( String : : formatted ( " Last " ) , level + 3 ) ;
break ;
}
} ;
if ( m_selector . word_selector_range . end . has_value ( ) ) {
print_indented ( " Range Start " , level + 2 ) ;
print_word_selector ( m_selector . word_selector_range . start ) ;
print_indented ( " Range End " , level + 2 ) ;
print_word_selector ( m_selector . word_selector_range . end . value ( ) ) ;
} else {
print_indented ( " Direct Address " , level + 2 ) ;
print_word_selector ( m_selector . word_selector_range . start ) ;
}
}
RefPtr < Value > HistoryEvent : : run ( RefPtr < Shell > shell )
{
if ( ! shell )
return create < AST : : ListValue > ( { } ) ;
auto editor = shell - > editor ( ) ;
if ( ! editor ) {
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " No history available! " , position ( ) ) ;
return create < AST : : ListValue > ( { } ) ;
}
auto & history = editor - > history ( ) ;
// FIXME: Implement reverse iterators and find()?
auto find_reverse = [ ] ( auto it_start , auto it_end , auto finder ) {
auto it = it_end ;
while ( it ! = it_start ) {
- - it ;
if ( finder ( * it ) )
return it ;
}
return it_end ;
} ;
// First, resolve the event itself.
String resolved_history ;
switch ( m_selector . event . kind ) {
case HistorySelector : : EventKind : : IndexFromStart :
if ( m_selector . event . index > = history . size ( ) ) {
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " History event index out of bounds " , m_selector . event . text_position ) ;
return create < AST : : ListValue > ( { } ) ;
}
resolved_history = history [ m_selector . event . index ] . entry ;
break ;
case HistorySelector : : EventKind : : IndexFromEnd :
if ( m_selector . event . index > = history . size ( ) ) {
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " History event index out of bounds " , m_selector . event . text_position ) ;
return create < AST : : ListValue > ( { } ) ;
}
resolved_history = history [ history . size ( ) - m_selector . event . index - 1 ] . entry ;
break ;
case HistorySelector : : EventKind : : ContainingStringLookup : {
auto it = find_reverse ( history . begin ( ) , history . end ( ) , [ & ] ( auto & entry ) { return entry . entry . contains ( m_selector . event . text ) ; } ) ;
if ( it . is_end ( ) ) {
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " History event did not match any entry " , m_selector . event . text_position ) ;
return create < AST : : ListValue > ( { } ) ;
}
resolved_history = it - > entry ;
break ;
}
case HistorySelector : : EventKind : : StartingStringLookup : {
auto it = find_reverse ( history . begin ( ) , history . end ( ) , [ & ] ( auto & entry ) { return entry . entry . starts_with ( m_selector . event . text ) ; } ) ;
if ( it . is_end ( ) ) {
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " History event did not match any entry " , m_selector . event . text_position ) ;
return create < AST : : ListValue > ( { } ) ;
}
resolved_history = it - > entry ;
break ;
}
}
// Then, split it up to "words".
auto nodes = Parser { resolved_history } . parse_as_multiple_expressions ( ) ;
// Now take the "words" as described by the word selectors.
bool is_range = m_selector . word_selector_range . end . has_value ( ) ;
if ( is_range ) {
auto start_index = m_selector . word_selector_range . start . resolve ( nodes . size ( ) ) ;
auto end_index = m_selector . word_selector_range . end - > resolve ( nodes . size ( ) ) ;
if ( start_index > = nodes . size ( ) ) {
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " History word index out of bounds " , m_selector . word_selector_range . start . position ) ;
return create < AST : : ListValue > ( { } ) ;
}
if ( end_index > = nodes . size ( ) ) {
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " History word index out of bounds " , m_selector . word_selector_range . end - > position ) ;
return create < AST : : ListValue > ( { } ) ;
}
decltype ( nodes ) resolved_nodes ;
resolved_nodes . append ( nodes . data ( ) + start_index , end_index - start_index + 1 ) ;
NonnullRefPtr < AST : : Node > list = create < AST : : ListConcatenate > ( position ( ) , move ( resolved_nodes ) ) ;
return list - > run ( shell ) ;
}
auto index = m_selector . word_selector_range . start . resolve ( nodes . size ( ) ) ;
if ( index > = nodes . size ( ) ) {
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " History word index out of bounds " , m_selector . word_selector_range . start . position ) ;
return create < AST : : ListValue > ( { } ) ;
}
return nodes [ index ] . run ( shell ) ;
}
void HistoryEvent : : highlight_in_editor ( Line : : Editor & editor , Shell & , HighlightMetadata metadata )
{
Line : : Style style { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Green ) } ;
if ( metadata . is_first_in_list )
style . unify_with ( { Line : : Style : : Bold } ) ;
editor . stylize ( { m_position . start_offset , m_position . end_offset } , move ( style ) ) ;
}
HistoryEvent : : HistoryEvent ( Position position , HistorySelector selector )
: Node ( move ( position ) )
, m_selector ( move ( selector ) )
{
2021-02-02 19:50:05 +03:00
if ( m_selector . word_selector_range . start . syntax_error_node )
set_is_syntax_error ( * m_selector . word_selector_range . start . syntax_error_node ) ;
else if ( m_selector . word_selector_range . end . has_value ( ) & & m_selector . word_selector_range . end - > syntax_error_node )
set_is_syntax_error ( * m_selector . word_selector_range . end - > syntax_error_node ) ;
2021-01-11 12:34:59 +03:00
}
HistoryEvent : : ~ HistoryEvent ( )
{
}
2020-06-17 16:35:06 +03:00
void Execute : : dump ( int level ) const
{
Node : : dump ( level ) ;
if ( m_capture_stdout )
print_indented ( " (Capturing stdout) " , level + 1 ) ;
m_command - > dump ( level + 1 ) ;
}
2020-09-16 03:37:14 +03:00
void Execute : : for_each_entry ( RefPtr < Shell > shell , Function < IterationDecision ( NonnullRefPtr < Value > ) > callback )
2020-06-17 16:35:06 +03:00
{
2020-06-24 06:15:24 +03:00
if ( m_command - > would_execute ( ) )
2020-08-21 02:18:23 +03:00
return m_command - > for_each_entry ( shell , move ( callback ) ) ;
2020-06-24 06:15:24 +03:00
2020-07-13 08:00:09 +03:00
auto commands = shell - > expand_aliases ( m_command - > run ( shell ) - > resolve_as_commands ( shell ) ) ;
2020-06-17 16:35:06 +03:00
if ( m_capture_stdout ) {
int pipefd [ 2 ] ;
int rc = pipe ( pipefd ) ;
if ( rc < 0 ) {
2020-12-06 20:21:40 +03:00
dbgln ( " Error: cannot pipe(): {} " , strerror ( errno ) ) ;
2020-08-21 02:18:23 +03:00
return ;
2020-06-17 16:35:06 +03:00
}
2020-06-21 23:00:14 +03:00
auto & last_in_commands = commands . last ( ) ;
2020-06-17 16:35:06 +03:00
2020-10-27 21:08:37 +03:00
last_in_commands . redirections . prepend ( FdRedirection : : create ( pipefd [ 1 ] , STDOUT_FILENO , Rewiring : : Close : : Old ) ) ;
2020-08-22 12:18:39 +03:00
last_in_commands . should_wait = false ;
2020-06-21 23:00:14 +03:00
last_in_commands . should_notify_if_in_background = false ;
last_in_commands . is_pipe_source = false ;
2020-06-17 16:35:06 +03:00
2020-08-22 12:18:39 +03:00
Core : : EventLoop loop ;
2020-06-17 16:35:06 +03:00
auto notifier = Core : : Notifier : : construct ( pipefd [ 0 ] , Core : : Notifier : : Read ) ;
2020-08-21 02:18:23 +03:00
DuplexMemoryStream stream ;
enum {
Continue ,
Break ,
NothingLeft ,
} ;
auto check_and_call = [ & ] {
auto ifs = shell - > local_variable_or ( " IFS " , " \n " ) ;
if ( auto offset = stream . offset_of ( ifs . bytes ( ) ) ; offset . has_value ( ) ) {
auto line_end = offset . value ( ) ;
if ( line_end = = 0 ) {
auto rc = stream . discard_or_error ( ifs . length ( ) ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( rc ) ;
2020-08-21 02:18:23 +03:00
if ( shell - > options . inline_exec_keep_empty_segments )
if ( callback ( create < StringValue > ( " " ) ) = = IterationDecision : : Break ) {
2020-08-22 12:18:39 +03:00
loop . quit ( Break ) ;
2020-08-21 02:18:23 +03:00
notifier - > set_enabled ( false ) ;
return Break ;
}
} else {
auto entry = ByteBuffer : : create_uninitialized ( line_end + ifs . length ( ) ) ;
auto rc = stream . read_or_error ( entry ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( rc ) ;
2020-08-21 02:18:23 +03:00
auto str = StringView ( entry . data ( ) , entry . size ( ) - ifs . length ( ) ) ;
if ( callback ( create < StringValue > ( str ) ) = = IterationDecision : : Break ) {
2020-08-22 12:18:39 +03:00
loop . quit ( Break ) ;
2020-08-21 02:18:23 +03:00
notifier - > set_enabled ( false ) ;
return Break ;
}
}
2020-06-17 16:35:06 +03:00
2020-08-21 02:18:23 +03:00
return Continue ;
}
return NothingLeft ;
} ;
2020-08-22 12:18:39 +03:00
notifier - > on_ready_to_read = [ & ] {
constexpr static auto buffer_size = 16 ;
2020-08-21 02:18:23 +03:00
u8 buffer [ buffer_size ] ;
size_t remaining_size = buffer_size ;
2020-06-17 16:35:06 +03:00
for ( ; ; ) {
2020-08-22 12:18:39 +03:00
notifier - > set_event_mask ( Core : : Notifier : : None ) ;
bool should_enable_notifier = false ;
ScopeGuard notifier_enabler { [ & ] {
if ( should_enable_notifier )
notifier - > set_event_mask ( Core : : Notifier : : Read ) ;
} } ;
2020-12-10 17:55:13 +03:00
if ( check_and_call ( ) = = Break ) {
loop . quit ( Break ) ;
2020-08-21 02:18:23 +03:00
return ;
2020-12-10 17:55:13 +03:00
}
2020-08-21 02:18:23 +03:00
2020-06-17 16:35:06 +03:00
auto read_size = read ( pipefd [ 0 ] , buffer , remaining_size ) ;
if ( read_size < 0 ) {
2020-08-22 12:18:39 +03:00
int saved_errno = errno ;
if ( saved_errno = = EINTR ) {
should_enable_notifier = true ;
2020-06-21 23:00:14 +03:00
continue ;
2020-08-22 12:18:39 +03:00
}
if ( saved_errno = = 0 )
continue ;
2020-12-06 20:21:40 +03:00
dbgln ( " read() failed: {} " , strerror ( saved_errno ) ) ;
2020-06-21 23:00:14 +03:00
break ;
2020-06-17 16:35:06 +03:00
}
if ( read_size = = 0 )
break ;
2020-08-22 12:18:39 +03:00
should_enable_notifier = true ;
2020-08-21 02:18:23 +03:00
stream . write ( { buffer , ( size_t ) read_size } ) ;
}
2020-06-17 16:35:06 +03:00
2020-12-10 17:55:13 +03:00
loop . quit ( NothingLeft ) ;
2020-06-21 23:00:14 +03:00
} ;
2020-12-10 17:55:13 +03:00
auto jobs = shell - > run_commands ( commands ) ;
ScopeGuard kill_jobs_if_around { [ & ] {
for ( auto & job : jobs ) {
if ( job . is_running_in_background ( ) & & ! job . exited ( ) & & ! job . signaled ( ) ) {
job . set_should_announce_signal ( false ) ; // We're explicitly killing it here.
shell - > kill_job ( & job , SIGTERM ) ;
}
}
} } ;
2020-06-17 16:35:06 +03:00
2020-12-10 17:55:13 +03:00
auto exit_reason = loop . exec ( ) ;
2020-06-17 16:35:06 +03:00
2020-08-22 12:18:39 +03:00
notifier - > on_ready_to_read = nullptr ;
2020-06-21 23:00:14 +03:00
2020-06-17 16:35:06 +03:00
if ( close ( pipefd [ 0 ] ) < 0 ) {
2020-12-06 20:21:40 +03:00
dbgln ( " close() failed: {} " , strerror ( errno ) ) ;
2020-06-17 16:35:06 +03:00
}
2020-12-10 17:55:13 +03:00
if ( exit_reason ! = Break & & ! stream . eof ( ) ) {
2020-08-21 02:18:23 +03:00
auto action = Continue ;
do {
action = check_and_call ( ) ;
if ( action = = Break )
return ;
} while ( action = = Continue ) ;
if ( ! stream . eof ( ) ) {
2020-09-15 12:48:54 +03:00
auto entry = ByteBuffer : : create_uninitialized ( stream . size ( ) ) ;
2020-08-21 02:18:23 +03:00
auto rc = stream . read_or_error ( entry ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( rc ) ;
2020-08-21 02:18:23 +03:00
callback ( create < StringValue > ( String : : copy ( entry ) ) ) ;
}
}
return ;
2020-06-17 16:35:06 +03:00
}
2020-09-01 07:12:16 +03:00
auto jobs = shell - > run_commands ( commands ) ;
2020-08-21 02:18:23 +03:00
2020-09-01 07:12:16 +03:00
if ( ! jobs . is_empty ( ) )
callback ( create < JobValue > ( & jobs . last ( ) ) ) ;
2020-08-21 02:18:23 +03:00
}
RefPtr < Value > Execute : : run ( RefPtr < Shell > shell )
{
2020-08-22 12:18:02 +03:00
if ( m_command - > would_execute ( ) )
return m_command - > run ( shell ) ;
2020-08-21 02:18:23 +03:00
NonnullRefPtrVector < Value > values ;
for_each_entry ( shell , [ & ] ( auto value ) {
values . append ( * value ) ;
return IterationDecision : : Continue ;
} ) ;
if ( values . size ( ) = = 1 & & values . first ( ) . is_job ( ) )
return values . first ( ) ;
return create < ListValue > ( move ( values ) ) ;
2020-06-17 16:35:06 +03:00
}
void Execute : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
if ( m_capture_stdout )
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Green ) } ) ;
metadata . is_first_in_list = true ;
m_command - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult Execute : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
auto result = m_command - > hit_test_position ( offset ) ;
if ( ! result . closest_node_with_semantic_meaning )
result . closest_node_with_semantic_meaning = this ;
2020-06-29 04:56:06 +03:00
if ( ! result . closest_command_node )
result . closest_command_node = m_command ;
2020-06-17 16:35:06 +03:00
return result ;
}
2020-06-29 04:56:06 +03:00
Vector < Line : : CompletionSuggestion > Execute : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
2020-06-17 16:35:06 +03:00
{
2020-06-29 04:56:06 +03:00
auto matching_node = hit_test_result . matching_node ;
2020-07-25 08:59:30 +03:00
if ( ! matching_node | | ! matching_node - > is_bareword ( ) )
2020-06-17 16:35:06 +03:00
return { } ;
auto corrected_offset = offset - matching_node - > position ( ) . start_offset ;
auto * node = static_cast < BarewordLiteral * > ( matching_node . ptr ( ) ) ;
if ( corrected_offset > node - > text ( ) . length ( ) )
return { } ;
return shell . complete_program_name ( node - > text ( ) , corrected_offset ) ;
}
2020-09-16 03:37:14 +03:00
Execute : : Execute ( Position position , NonnullRefPtr < Node > command , bool capture_stdout )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_command ( move ( command ) )
, m_capture_stdout ( capture_stdout )
{
2020-06-23 17:40:41 +03:00
if ( m_command - > is_syntax_error ( ) )
2020-06-28 17:12:57 +03:00
set_is_syntax_error ( m_command - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
Execute : : ~ Execute ( )
{
}
2020-08-11 10:35:46 +03:00
void IfCond : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( " Condition " , + + level ) ;
m_condition - > dump ( level + 1 ) ;
print_indented ( " True Branch " , level ) ;
if ( m_true_branch )
m_true_branch - > dump ( level + 1 ) ;
else
print_indented ( " (empty) " , level + 1 ) ;
print_indented ( " False Branch " , level ) ;
if ( m_false_branch )
m_false_branch - > dump ( level + 1 ) ;
else
print_indented ( " (empty) " , level + 1 ) ;
}
RefPtr < Value > IfCond : : run ( RefPtr < Shell > shell )
{
auto cond = m_condition - > run ( shell ) - > resolve_without_cast ( shell ) ;
2020-09-14 13:27:30 +03:00
// The condition could be a builtin, in which case it has already run and exited.
2021-06-28 17:29:06 +03:00
if ( cond - > is_job ( ) ) {
2020-09-14 13:27:30 +03:00
auto cond_job_value = static_cast < const JobValue * > ( cond . ptr ( ) ) ;
auto cond_job = cond_job_value - > job ( ) ;
2020-08-11 10:35:46 +03:00
2020-09-14 13:27:30 +03:00
shell - > block_on_job ( cond_job ) ;
}
2020-09-01 07:12:16 +03:00
if ( shell - > last_return_code = = 0 ) {
2020-08-11 10:35:46 +03:00
if ( m_true_branch )
return m_true_branch - > run ( shell ) ;
} else {
if ( m_false_branch )
return m_false_branch - > run ( shell ) ;
}
return create < ListValue > ( { } ) ;
}
void IfCond : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
metadata . is_first_in_list = true ;
editor . stylize ( { m_position . start_offset , m_position . start_offset + 2 } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
if ( m_else_position . has_value ( ) )
editor . stylize ( { m_else_position . value ( ) . start_offset , m_else_position . value ( ) . start_offset + 4 } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
m_condition - > highlight_in_editor ( editor , shell , metadata ) ;
if ( m_true_branch )
m_true_branch - > highlight_in_editor ( editor , shell , metadata ) ;
if ( m_false_branch )
m_false_branch - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult IfCond : : hit_test_position ( size_t offset ) const
2020-08-11 10:35:46 +03:00
{
if ( auto result = m_condition - > hit_test_position ( offset ) ; result . matching_node )
return result ;
if ( m_true_branch ) {
if ( auto result = m_true_branch - > hit_test_position ( offset ) ; result . matching_node )
return result ;
}
if ( m_false_branch ) {
if ( auto result = m_false_branch - > hit_test_position ( offset ) ; result . matching_node )
return result ;
}
return { } ;
}
2020-09-16 03:37:14 +03:00
IfCond : : IfCond ( Position position , Optional < Position > else_position , NonnullRefPtr < Node > condition , RefPtr < Node > true_branch , RefPtr < Node > false_branch )
2020-08-11 10:35:46 +03:00
: Node ( move ( position ) )
, m_condition ( move ( condition ) )
, m_true_branch ( move ( true_branch ) )
, m_false_branch ( move ( false_branch ) )
, m_else_position ( move ( else_position ) )
{
if ( m_condition - > is_syntax_error ( ) )
set_is_syntax_error ( m_condition - > syntax_error_node ( ) ) ;
else if ( m_true_branch & & m_true_branch - > is_syntax_error ( ) )
set_is_syntax_error ( m_true_branch - > syntax_error_node ( ) ) ;
else if ( m_false_branch & & m_false_branch - > is_syntax_error ( ) )
set_is_syntax_error ( m_false_branch - > syntax_error_node ( ) ) ;
m_condition = create < AST : : Execute > ( m_condition - > position ( ) , m_condition ) ;
2020-09-16 03:37:14 +03:00
if ( m_true_branch ) {
auto true_branch = m_true_branch . release_nonnull ( ) ;
2021-01-18 09:36:13 +03:00
if ( true_branch - > is_execute ( ) )
m_true_branch = static_ptr_cast < AST : : Execute > ( true_branch ) - > command ( ) ;
else
m_true_branch = move ( true_branch ) ;
2020-09-16 03:37:14 +03:00
}
if ( m_false_branch ) {
auto false_branch = m_false_branch . release_nonnull ( ) ;
2021-01-18 09:36:13 +03:00
if ( false_branch - > is_execute ( ) )
m_false_branch = static_ptr_cast < AST : : Execute > ( false_branch ) - > command ( ) ;
else
m_false_branch = move ( false_branch ) ;
2020-09-16 03:37:14 +03:00
}
2020-08-11 10:35:46 +03:00
}
IfCond : : ~ IfCond ( )
{
}
2021-03-05 16:03:23 +03:00
void ImmediateExpression : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( " (function) " , level + 1 ) ;
print_indented ( m_function . name , level + 2 ) ;
print_indented ( " (arguments) " , level + 1 ) ;
for ( auto & argument : arguments ( ) )
argument . dump ( level + 2 ) ;
}
RefPtr < Value > ImmediateExpression : : run ( RefPtr < Shell > shell )
{
auto node = shell - > run_immediate_function ( m_function . name , * this , arguments ( ) ) ;
if ( node )
return node - > run ( shell ) ;
return create < ListValue > ( { } ) ;
}
void ImmediateExpression : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
// '${' - FIXME: This could also be '$\\\n{'
editor . stylize ( { m_position . start_offset , m_position . start_offset + 2 } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Green ) } ) ;
// Function name
Line : : Style function_style { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Red ) } ;
if ( shell . has_immediate_function ( function_name ( ) ) )
function_style = { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Green ) } ;
editor . stylize ( { m_function . position . start_offset , m_function . position . end_offset } , move ( function_style ) ) ;
// Arguments
for ( auto & argument : m_arguments ) {
metadata . is_first_in_list = false ;
argument . highlight_in_editor ( editor , shell , metadata ) ;
}
// Closing brace
if ( m_closing_brace_position . has_value ( ) )
editor . stylize ( { m_closing_brace_position - > start_offset , m_closing_brace_position - > end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Green ) } ) ;
}
Vector < Line : : CompletionSuggestion > ImmediateExpression : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
{
auto matching_node = hit_test_result . matching_node ;
if ( ! matching_node | | matching_node ! = this )
return { } ;
auto corrected_offset = offset - m_function . position . start_offset ;
if ( corrected_offset > m_function . name . length ( ) )
return { } ;
return shell . complete_immediate_function_name ( m_function . name , corrected_offset ) ;
}
HitTestResult ImmediateExpression : : hit_test_position ( size_t offset ) const
{
if ( m_function . position . contains ( offset ) )
return { this , this , this } ;
for ( auto & argument : m_arguments ) {
if ( auto result = argument . hit_test_position ( offset ) ; result . matching_node )
return result ;
}
return { } ;
}
ImmediateExpression : : ImmediateExpression ( Position position , NameWithPosition function , NonnullRefPtrVector < AST : : Node > arguments , Optional < Position > closing_brace_position )
: Node ( move ( position ) )
, m_arguments ( move ( arguments ) )
, m_function ( move ( function ) )
, m_closing_brace_position ( move ( closing_brace_position ) )
{
2021-04-29 05:28:46 +03:00
if ( is_syntax_error ( ) )
2021-03-05 16:03:23 +03:00
return ;
for ( auto & argument : m_arguments ) {
if ( argument . is_syntax_error ( ) ) {
set_is_syntax_error ( argument . syntax_error_node ( ) ) ;
return ;
}
}
}
ImmediateExpression : : ~ ImmediateExpression ( )
{
}
2020-06-17 16:35:06 +03:00
void Join : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_left - > dump ( level + 1 ) ;
m_right - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Join : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-09-07 19:19:53 +03:00
auto left = m_left - > to_lazy_evaluated_commands ( shell ) ;
auto right = m_right - > to_lazy_evaluated_commands ( shell ) ;
2020-06-17 16:35:06 +03:00
2020-06-22 14:07:20 +03:00
return create < CommandSequenceValue > ( join_commands ( move ( left ) , move ( right ) ) ) ;
2020-06-17 16:35:06 +03:00
}
void Join : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
m_left - > highlight_in_editor ( editor , shell , metadata ) ;
if ( m_left - > is_list ( ) | | m_left - > is_command ( ) )
metadata . is_first_in_list = false ;
m_right - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult Join : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
auto result = m_left - > hit_test_position ( offset ) ;
if ( result . matching_node )
return result ;
2020-09-16 03:37:14 +03:00
2020-06-17 16:35:06 +03:00
return m_right - > hit_test_position ( offset ) ;
}
2020-06-29 04:56:06 +03:00
RefPtr < Node > Join : : leftmost_trivial_literal ( ) const
{
if ( auto value = m_left - > leftmost_trivial_literal ( ) )
return value ;
return m_right - > leftmost_trivial_literal ( ) ;
}
2020-09-16 03:37:14 +03:00
Join : : Join ( Position position , NonnullRefPtr < Node > left , NonnullRefPtr < Node > right )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_left ( move ( left ) )
, m_right ( move ( right ) )
{
2020-06-28 17:12:57 +03:00
if ( m_left - > is_syntax_error ( ) )
set_is_syntax_error ( m_left - > syntax_error_node ( ) ) ;
else if ( m_right - > is_syntax_error ( ) )
set_is_syntax_error ( m_right - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
Join : : ~ Join ( )
{
}
2020-09-14 18:02:21 +03:00
void MatchExpr : : dump ( int level ) const
{
Node : : dump ( level ) ;
2021-01-11 16:30:22 +03:00
print_indented ( String : : formatted ( " (expression: {}) " , m_expr_name . characters ( ) ) , level + 1 ) ;
2020-09-14 18:02:21 +03:00
m_matched_expr - > dump ( level + 2 ) ;
2021-01-11 16:30:22 +03:00
print_indented ( String : : formatted ( " (named: {}) " , m_expr_name . characters ( ) ) , level + 1 ) ;
2020-09-14 18:02:21 +03:00
print_indented ( " (entries) " , level + 1 ) ;
for ( auto & entry : m_entries ) {
2020-10-25 10:12:03 +03:00
StringBuilder builder ;
builder . append ( " (match " ) ;
if ( entry . match_names . has_value ( ) ) {
builder . append ( " to names ( " ) ;
bool first = true ;
for ( auto & name : entry . match_names . value ( ) ) {
if ( ! first )
builder . append ( ' ' ) ;
first = false ;
builder . append ( name ) ;
}
builder . append ( " )) " ) ;
} else {
builder . append ( ' ) ' ) ;
}
print_indented ( builder . string_view ( ) , level + 2 ) ;
2020-09-14 18:02:21 +03:00
for ( auto & node : entry . options )
node . dump ( level + 3 ) ;
print_indented ( " (execute) " , level + 2 ) ;
if ( entry . body )
entry . body - > dump ( level + 3 ) ;
else
print_indented ( " (nothing) " , level + 3 ) ;
}
}
RefPtr < Value > MatchExpr : : run ( RefPtr < Shell > shell )
{
auto value = m_matched_expr - > run ( shell ) - > resolve_without_cast ( shell ) ;
auto list = value - > resolve_as_list ( shell ) ;
2020-10-25 10:12:03 +03:00
auto list_matches = [ & ] ( auto & & pattern , auto & spans ) {
2020-09-14 18:02:21 +03:00
if ( pattern . size ( ) ! = list . size ( ) )
return false ;
for ( size_t i = 0 ; i < pattern . size ( ) ; + + i ) {
2020-10-25 10:12:03 +03:00
Vector < AK : : MaskSpan > mask_spans ;
if ( ! list [ i ] . matches ( pattern [ i ] , mask_spans ) )
2020-09-14 18:02:21 +03:00
return false ;
2020-10-25 10:12:03 +03:00
for ( auto & span : mask_spans )
spans . append ( list [ i ] . substring ( span . start , span . length ) ) ;
2020-09-14 18:02:21 +03:00
}
return true ;
} ;
auto resolve_pattern = [ & ] ( auto & option ) {
Vector < String > pattern ;
if ( option . is_glob ( ) ) {
pattern . append ( static_cast < const Glob * > ( & option ) - > text ( ) ) ;
} else if ( option . is_bareword ( ) ) {
pattern . append ( static_cast < const BarewordLiteral * > ( & option ) - > text ( ) ) ;
2020-10-25 10:12:03 +03:00
} else {
2020-09-14 18:02:21 +03:00
auto list = option . run ( shell ) ;
option . for_each_entry ( shell , [ & ] ( auto & & value ) {
2021-06-12 14:24:45 +03:00
pattern . extend ( value - > resolve_as_list ( nullptr ) ) ; // Note: 'nullptr' incurs special behaviour,
2020-09-14 18:02:21 +03:00
// asking the node for a 'raw' value.
return IterationDecision : : Continue ;
} ) ;
}
return pattern ;
} ;
2020-11-01 12:58:11 +03:00
auto frame = shell - > push_frame ( String : : formatted ( " match ({}) " , this ) ) ;
2020-09-14 18:02:21 +03:00
if ( ! m_expr_name . is_empty ( ) )
2020-12-09 15:04:55 +03:00
shell - > set_local_variable ( m_expr_name , value , true ) ;
2020-09-14 18:02:21 +03:00
for ( auto & entry : m_entries ) {
for ( auto & option : entry . options ) {
2020-10-25 10:12:03 +03:00
Vector < String > spans ;
if ( list_matches ( resolve_pattern ( option ) , spans ) ) {
if ( entry . body ) {
if ( entry . match_names . has_value ( ) ) {
size_t i = 0 ;
for ( auto & name : entry . match_names . value ( ) ) {
if ( spans . size ( ) > i )
2020-12-09 15:04:55 +03:00
shell - > set_local_variable ( name , create < AST : : StringValue > ( spans [ i ] ) , true ) ;
2020-10-25 10:12:03 +03:00
+ + i ;
}
}
2020-09-14 18:02:21 +03:00
return entry . body - > run ( shell ) ;
2020-10-25 10:12:03 +03:00
} else {
2020-09-14 18:02:21 +03:00
return create < AST : : ListValue > ( { } ) ;
2020-10-25 10:12:03 +03:00
}
2020-09-14 18:02:21 +03:00
}
}
}
2021-01-03 12:08:20 +03:00
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " Non-exhaustive match rules! " , position ( ) ) ;
2020-09-14 18:02:21 +03:00
return create < AST : : ListValue > ( { } ) ;
}
void MatchExpr : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
editor . stylize ( { m_position . start_offset , m_position . start_offset + 5 } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
if ( m_as_position . has_value ( ) )
editor . stylize ( { m_as_position . value ( ) . start_offset , m_as_position . value ( ) . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
metadata . is_first_in_list = false ;
2020-09-16 03:37:14 +03:00
m_matched_expr - > highlight_in_editor ( editor , shell , metadata ) ;
2020-09-14 18:02:21 +03:00
for ( auto & entry : m_entries ) {
metadata . is_first_in_list = false ;
for ( auto & option : entry . options )
option . highlight_in_editor ( editor , shell , metadata ) ;
metadata . is_first_in_list = true ;
if ( entry . body )
entry . body - > highlight_in_editor ( editor , shell , metadata ) ;
for ( auto & position : entry . pipe_positions )
editor . stylize ( { position . start_offset , position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
2020-10-25 10:12:03 +03:00
if ( entry . match_as_position . has_value ( ) )
editor . stylize ( { entry . match_as_position . value ( ) . start_offset , entry . match_as_position . value ( ) . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
2020-09-14 18:02:21 +03:00
}
}
2021-03-02 09:05:04 +03:00
HitTestResult MatchExpr : : hit_test_position ( size_t offset ) const
2020-09-14 18:02:21 +03:00
{
auto result = m_matched_expr - > hit_test_position ( offset ) ;
if ( result . matching_node )
return result ;
for ( auto & entry : m_entries ) {
if ( ! entry . body )
continue ;
auto result = entry . body - > hit_test_position ( offset ) ;
if ( result . matching_node )
return result ;
}
return { } ;
}
2020-09-16 03:37:14 +03:00
MatchExpr : : MatchExpr ( Position position , NonnullRefPtr < Node > expr , String name , Optional < Position > as_position , Vector < MatchEntry > entries )
2020-09-14 18:02:21 +03:00
: Node ( move ( position ) )
, m_matched_expr ( move ( expr ) )
, m_expr_name ( move ( name ) )
, m_as_position ( move ( as_position ) )
, m_entries ( move ( entries ) )
{
2020-09-16 03:37:14 +03:00
if ( m_matched_expr - > is_syntax_error ( ) ) {
2020-09-14 18:02:21 +03:00
set_is_syntax_error ( m_matched_expr - > syntax_error_node ( ) ) ;
} else {
for ( auto & entry : m_entries ) {
if ( ! entry . body )
continue ;
if ( entry . body - > is_syntax_error ( ) ) {
set_is_syntax_error ( entry . body - > syntax_error_node ( ) ) ;
break ;
}
}
}
}
MatchExpr : : ~ MatchExpr ( )
{
}
2020-06-17 16:35:06 +03:00
void Or : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_left - > dump ( level + 1 ) ;
m_right - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Or : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-09-07 19:19:53 +03:00
auto commands = m_left - > to_lazy_evaluated_commands ( shell ) ;
2020-09-01 07:12:16 +03:00
commands . last ( ) . next_chain . empend ( * m_right , NodeWithAction : : Or ) ;
return create < CommandSequenceValue > ( move ( commands ) ) ;
2020-06-17 16:35:06 +03:00
}
void Or : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
m_left - > highlight_in_editor ( editor , shell , metadata ) ;
m_right - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult Or : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
auto result = m_left - > hit_test_position ( offset ) ;
2020-06-29 04:56:06 +03:00
if ( result . matching_node ) {
if ( ! result . closest_command_node )
result . closest_command_node = m_right ;
2020-06-17 16:35:06 +03:00
return result ;
2020-06-29 04:56:06 +03:00
}
2020-09-16 03:37:14 +03:00
2020-06-29 04:56:06 +03:00
result = m_right - > hit_test_position ( offset ) ;
if ( ! result . closest_command_node )
result . closest_command_node = m_right ;
return result ;
2020-06-17 16:35:06 +03:00
}
2020-09-28 13:57:20 +03:00
Or : : Or ( Position position , NonnullRefPtr < Node > left , NonnullRefPtr < Node > right , Position or_position )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_left ( move ( left ) )
, m_right ( move ( right ) )
2020-09-28 13:57:20 +03:00
, m_or_position ( or_position )
2020-06-17 16:35:06 +03:00
{
2020-06-28 17:12:57 +03:00
if ( m_left - > is_syntax_error ( ) )
set_is_syntax_error ( m_left - > syntax_error_node ( ) ) ;
else if ( m_right - > is_syntax_error ( ) )
set_is_syntax_error ( m_right - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
Or : : ~ Or ( )
{
}
void Pipe : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_left - > dump ( level + 1 ) ;
m_right - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Pipe : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-09-07 19:19:53 +03:00
auto left = m_left - > to_lazy_evaluated_commands ( shell ) ;
auto right = m_right - > to_lazy_evaluated_commands ( shell ) ;
2020-06-17 16:35:06 +03:00
auto last_in_left = left . take_last ( ) ;
auto first_in_right = right . take_first ( ) ;
2020-10-27 21:08:37 +03:00
auto pipe_read_end = FdRedirection : : create ( - 1 , STDIN_FILENO , Rewiring : : Close : : Old ) ;
auto pipe_write_end = FdRedirection : : create ( - 1 , STDOUT_FILENO , pipe_read_end , Rewiring : : Close : : RefreshOld ) ;
2021-03-30 01:30:12 +03:00
auto insert_at_start_or_after_last_pipe = [ & ] ( auto & pipe , auto & command ) {
size_t insert_index = 0 ;
auto & redirections = command . redirections ;
for ( ssize_t i = redirections . size ( ) - 1 ; i > = 0 ; - - i ) {
auto & redirection = redirections [ i ] ;
if ( ! redirection . is_fd_redirection ( ) )
continue ;
auto & fd_redirection = static_cast < FdRedirection & > ( redirection ) ;
if ( fd_redirection . old_fd = = - 1 ) {
insert_index = i ;
break ;
}
}
redirections . insert ( insert_index , pipe ) ;
} ;
insert_at_start_or_after_last_pipe ( pipe_read_end , first_in_right ) ;
insert_at_start_or_after_last_pipe ( pipe_write_end , last_in_left ) ;
2020-06-17 16:35:06 +03:00
last_in_left . should_wait = false ;
last_in_left . is_pipe_source = true ;
2020-08-11 14:24:46 +03:00
if ( first_in_right . pipeline ) {
last_in_left . pipeline = first_in_right . pipeline ;
} else {
2021-01-03 12:05:06 +03:00
auto pipeline = create < Pipeline > ( ) ;
2020-08-11 14:24:46 +03:00
last_in_left . pipeline = pipeline ;
first_in_right . pipeline = pipeline ;
}
2020-06-17 16:35:06 +03:00
Vector < Command > commands ;
2021-06-12 14:24:45 +03:00
commands . extend ( left ) ;
2020-06-17 16:35:06 +03:00
commands . append ( last_in_left ) ;
commands . append ( first_in_right ) ;
2021-06-12 14:24:45 +03:00
commands . extend ( right ) ;
2020-06-17 16:35:06 +03:00
2020-06-20 16:30:45 +03:00
return create < CommandSequenceValue > ( move ( commands ) ) ;
2020-06-17 16:35:06 +03:00
}
void Pipe : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
m_left - > highlight_in_editor ( editor , shell , metadata ) ;
m_right - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult Pipe : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
auto result = m_left - > hit_test_position ( offset ) ;
2020-10-04 10:21:28 +03:00
if ( result . matching_node ) {
if ( ! result . closest_command_node )
result . closest_command_node = m_right ;
2020-06-17 16:35:06 +03:00
return result ;
2020-10-04 10:21:28 +03:00
}
2020-09-16 03:37:14 +03:00
2020-10-04 10:21:28 +03:00
result = m_right - > hit_test_position ( offset ) ;
if ( ! result . closest_command_node )
result . closest_command_node = m_right ;
return result ;
2020-06-17 16:35:06 +03:00
}
2020-09-16 03:37:14 +03:00
Pipe : : Pipe ( Position position , NonnullRefPtr < Node > left , NonnullRefPtr < Node > right )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_left ( move ( left ) )
, m_right ( move ( right ) )
{
2020-06-28 17:12:57 +03:00
if ( m_left - > is_syntax_error ( ) )
set_is_syntax_error ( m_left - > syntax_error_node ( ) ) ;
else if ( m_right - > is_syntax_error ( ) )
set_is_syntax_error ( m_right - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
Pipe : : ~ Pipe ( )
{
}
2020-09-16 03:37:14 +03:00
PathRedirectionNode : : PathRedirectionNode ( Position position , int fd , NonnullRefPtr < Node > path )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_fd ( fd )
, m_path ( move ( path ) )
{
}
void PathRedirectionNode : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( 0x87 , 0x9b , 0xcd ) } ) ; // 25% Darkened Periwinkle
metadata . is_first_in_list = false ;
m_path - > highlight_in_editor ( editor , shell , metadata ) ;
if ( m_path - > is_bareword ( ) ) {
auto path_text = m_path - > run ( nullptr ) - > resolve_as_list ( nullptr ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( path_text . size ( ) = = 1 ) ;
2020-06-17 16:35:06 +03:00
// Apply a URL to the path.
auto & position = m_path - > position ( ) ;
auto & path = path_text [ 0 ] ;
if ( ! path . starts_with ( ' / ' ) )
2021-04-21 23:16:14 +03:00
path = String : : formatted ( " {}/{} " , shell . cwd , path ) ;
2020-06-17 16:35:06 +03:00
auto url = URL : : create_with_file_protocol ( path ) ;
url . set_host ( shell . hostname ) ;
editor . stylize ( { position . start_offset , position . end_offset } , { Line : : Style : : Hyperlink ( url . to_string ( ) ) } ) ;
}
}
2021-03-02 09:05:04 +03:00
HitTestResult PathRedirectionNode : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
auto result = m_path - > hit_test_position ( offset ) ;
if ( ! result . closest_node_with_semantic_meaning )
result . closest_node_with_semantic_meaning = this ;
return result ;
}
2020-06-29 04:56:06 +03:00
Vector < Line : : CompletionSuggestion > PathRedirectionNode : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
2020-06-17 16:35:06 +03:00
{
2020-06-29 04:56:06 +03:00
auto matching_node = hit_test_result . matching_node ;
2020-07-25 08:59:30 +03:00
if ( ! matching_node | | ! matching_node - > is_bareword ( ) )
2020-06-17 16:35:06 +03:00
return { } ;
auto corrected_offset = offset - matching_node - > position ( ) . start_offset ;
auto * node = static_cast < BarewordLiteral * > ( matching_node . ptr ( ) ) ;
if ( corrected_offset > node - > text ( ) . length ( ) )
return { } ;
2021-04-20 17:17:38 +03:00
return shell . complete_path ( " " , node - > text ( ) , corrected_offset , Shell : : ExecutableOnly : : No ) ;
2020-06-17 16:35:06 +03:00
}
PathRedirectionNode : : ~ PathRedirectionNode ( )
{
}
2020-10-24 17:43:02 +03:00
void Range : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( " (From) " , level + 1 ) ;
m_start - > dump ( level + 2 ) ;
print_indented ( " (To) " , level + 1 ) ;
m_end - > dump ( level + 2 ) ;
}
RefPtr < Value > Range : : run ( RefPtr < Shell > shell )
{
2021-01-03 12:08:20 +03:00
auto interpolate = [ position = position ( ) ] ( RefPtr < Value > start , RefPtr < Value > end , RefPtr < Shell > shell ) - > NonnullRefPtrVector < Value > {
2020-10-24 17:43:02 +03:00
NonnullRefPtrVector < Value > values ;
if ( start - > is_string ( ) & & end - > is_string ( ) ) {
auto start_str = start - > resolve_as_list ( shell ) [ 0 ] ;
auto end_str = end - > resolve_as_list ( shell ) [ 0 ] ;
Utf8View start_view { start_str } , end_view { end_str } ;
if ( start_view . validate ( ) & & end_view . validate ( ) ) {
if ( start_view . length ( ) = = 1 & & end_view . length ( ) = = 1 ) {
// Interpolate between two code points.
auto start_code_point = * start_view . begin ( ) ;
auto end_code_point = * end_view . begin ( ) ;
auto step = start_code_point > end_code_point ? - 1 : 1 ;
StringBuilder builder ;
for ( u32 code_point = start_code_point ; code_point ! = end_code_point ; code_point + = step ) {
builder . clear ( ) ;
builder . append_code_point ( code_point ) ;
values . append ( create < StringValue > ( builder . to_string ( ) ) ) ;
}
// Append the ending code point too, most shells treat this as inclusive.
builder . clear ( ) ;
builder . append_code_point ( end_code_point ) ;
values . append ( create < StringValue > ( builder . to_string ( ) ) ) ;
} else {
// Could be two numbers?
auto start_int = start_str . to_int ( ) ;
auto end_int = end_str . to_int ( ) ;
if ( start_int . has_value ( ) & & end_int . has_value ( ) ) {
auto start = start_int . value ( ) ;
auto end = end_int . value ( ) ;
2020-12-29 16:58:46 +03:00
auto step = start > end ? - 1 : 1 ;
2020-10-24 17:43:02 +03:00
for ( int value = start ; value ! = end ; value + = step )
values . append ( create < StringValue > ( String : : number ( value ) ) ) ;
// Append the range end too, most shells treat this as inclusive.
values . append ( create < StringValue > ( String : : number ( end ) ) ) ;
} else {
goto yield_start_end ;
}
}
} else {
yield_start_end : ;
2021-01-03 12:08:20 +03:00
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , String : : formatted ( " Cannot interpolate between '{}' and '{}'! " , start_str , end_str ) , position ) ;
2020-10-24 17:43:02 +03:00
// We can't really interpolate between the two, so just yield both.
values . append ( create < StringValue > ( move ( start_str ) ) ) ;
values . append ( create < StringValue > ( move ( end_str ) ) ) ;
}
return values ;
}
warnln ( " Shell: Cannot apply the requested interpolation " ) ;
return values ;
} ;
auto start_value = m_start - > run ( shell ) ;
auto end_value = m_end - > run ( shell ) ;
if ( ! start_value | | ! end_value )
return create < ListValue > ( { } ) ;
return create < ListValue > ( interpolate ( * start_value , * end_value , shell ) ) ;
}
void Range : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
m_start - > highlight_in_editor ( editor , shell , metadata ) ;
// Highlight the '..'
editor . stylize ( { m_start - > position ( ) . end_offset , m_end - > position ( ) . start_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ) ;
metadata . is_first_in_list = false ;
m_end - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult Range : : hit_test_position ( size_t offset ) const
2020-10-24 17:43:02 +03:00
{
auto result = m_start - > hit_test_position ( offset ) ;
if ( result . matching_node ) {
if ( ! result . closest_command_node )
result . closest_command_node = m_start ;
return result ;
}
result = m_end - > hit_test_position ( offset ) ;
if ( ! result . closest_command_node )
result . closest_command_node = m_end ;
return result ;
}
Range : : Range ( Position position , NonnullRefPtr < Node > start , NonnullRefPtr < Node > end )
: Node ( move ( position ) )
, m_start ( move ( start ) )
, m_end ( move ( end ) )
{
if ( m_start - > is_syntax_error ( ) )
set_is_syntax_error ( m_start - > syntax_error_node ( ) ) ;
else if ( m_end - > is_syntax_error ( ) )
set_is_syntax_error ( m_end - > syntax_error_node ( ) ) ;
}
Range : : ~ Range ( )
{
}
2020-06-17 16:35:06 +03:00
void ReadRedirection : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_path - > dump ( level + 1 ) ;
2021-04-21 23:16:14 +03:00
print_indented ( String : : formatted ( " To {} " , m_fd ) , level + 1 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > ReadRedirection : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
Command command ;
2020-06-23 17:40:41 +03:00
auto path_segments = m_path - > run ( shell ) - > resolve_as_list ( shell ) ;
2020-06-17 16:35:06 +03:00
StringBuilder builder ;
builder . join ( " " , path_segments ) ;
2020-08-12 13:15:30 +03:00
command . redirections . append ( PathRedirection : : create ( builder . to_string ( ) , m_fd , PathRedirection : : Read ) ) ;
2020-06-20 16:30:45 +03:00
return create < CommandValue > ( move ( command ) ) ;
2020-06-17 16:35:06 +03:00
}
2020-09-16 03:37:14 +03:00
ReadRedirection : : ReadRedirection ( Position position , int fd , NonnullRefPtr < Node > path )
2020-06-17 16:35:06 +03:00
: PathRedirectionNode ( move ( position ) , fd , move ( path ) )
{
}
ReadRedirection : : ~ ReadRedirection ( )
{
}
void ReadWriteRedirection : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_path - > dump ( level + 1 ) ;
2021-04-21 23:16:14 +03:00
print_indented ( String : : formatted ( " To/From {} " , m_fd ) , level + 1 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > ReadWriteRedirection : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
Command command ;
2020-06-23 17:40:41 +03:00
auto path_segments = m_path - > run ( shell ) - > resolve_as_list ( shell ) ;
2020-06-17 16:35:06 +03:00
StringBuilder builder ;
builder . join ( " " , path_segments ) ;
2020-08-12 13:15:30 +03:00
command . redirections . append ( PathRedirection : : create ( builder . to_string ( ) , m_fd , PathRedirection : : ReadWrite ) ) ;
2020-06-20 16:30:45 +03:00
return create < CommandValue > ( move ( command ) ) ;
2020-06-17 16:35:06 +03:00
}
2020-09-16 03:37:14 +03:00
ReadWriteRedirection : : ReadWriteRedirection ( Position position , int fd , NonnullRefPtr < Node > path )
2020-06-17 16:35:06 +03:00
: PathRedirectionNode ( move ( position ) , fd , move ( path ) )
{
}
ReadWriteRedirection : : ~ ReadWriteRedirection ( )
{
}
void Sequence : : dump ( int level ) const
{
Node : : dump ( level ) ;
2021-01-16 22:50:52 +03:00
for ( auto & entry : m_entries )
entry . dump ( level + 1 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Sequence : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2021-01-16 22:50:52 +03:00
Vector < Command > all_commands ;
Command * last_command_in_sequence = nullptr ;
for ( auto & entry : m_entries ) {
if ( ! last_command_in_sequence ) {
auto commands = entry . to_lazy_evaluated_commands ( shell ) ;
2021-06-12 14:24:45 +03:00
all_commands . extend ( move ( commands ) ) ;
2021-01-16 22:50:52 +03:00
last_command_in_sequence = & all_commands . last ( ) ;
continue ;
}
2020-07-05 18:18:26 +03:00
2021-01-16 22:50:52 +03:00
if ( last_command_in_sequence - > should_wait ) {
last_command_in_sequence - > next_chain . append ( NodeWithAction { entry , NodeWithAction : : Sequence } ) ;
} else {
2021-06-12 14:24:45 +03:00
all_commands . extend ( entry . to_lazy_evaluated_commands ( shell ) ) ;
2021-01-16 22:50:52 +03:00
last_command_in_sequence = & all_commands . last ( ) ;
}
}
2020-06-17 16:35:06 +03:00
2021-01-16 22:50:52 +03:00
return create < CommandSequenceValue > ( move ( all_commands ) ) ;
2020-06-17 16:35:06 +03:00
}
void Sequence : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
2021-01-16 22:50:52 +03:00
for ( auto & entry : m_entries )
entry . highlight_in_editor ( editor , shell , metadata ) ;
2020-06-17 16:35:06 +03:00
}
2021-03-02 09:05:04 +03:00
HitTestResult Sequence : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
2021-01-16 22:50:52 +03:00
for ( auto & entry : m_entries ) {
auto result = entry . hit_test_position ( offset ) ;
if ( result . matching_node ) {
if ( ! result . closest_command_node )
result . closest_command_node = entry ;
return result ;
}
2020-10-04 10:21:28 +03:00
}
2021-01-16 22:50:52 +03:00
return { } ;
2020-06-17 16:35:06 +03:00
}
2021-01-16 22:50:52 +03:00
Sequence : : Sequence ( Position position , NonnullRefPtrVector < Node > entries , Vector < Position > separator_positions )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
2021-01-16 22:50:52 +03:00
, m_entries ( move ( entries ) )
, m_separator_positions ( separator_positions )
2020-06-17 16:35:06 +03:00
{
2021-01-16 22:50:52 +03:00
for ( auto & entry : m_entries ) {
if ( entry . is_syntax_error ( ) ) {
set_is_syntax_error ( entry . syntax_error_node ( ) ) ;
break ;
}
}
2020-06-17 16:35:06 +03:00
}
Sequence : : ~ Sequence ( )
{
}
2020-09-08 14:29:07 +03:00
void Subshell : : dump ( int level ) const
{
Node : : dump ( level ) ;
if ( m_block )
m_block - > dump ( level + 1 ) ;
}
RefPtr < Value > Subshell : : run ( RefPtr < Shell > shell )
{
if ( ! m_block )
return create < ListValue > ( { } ) ;
2020-12-14 22:39:17 +03:00
return create < AST : : CommandSequenceValue > ( m_block - > to_lazy_evaluated_commands ( shell ) ) ;
2020-09-08 14:29:07 +03:00
}
void Subshell : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
metadata . is_first_in_list = true ;
if ( m_block )
m_block - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult Subshell : : hit_test_position ( size_t offset ) const
2020-09-08 14:29:07 +03:00
{
if ( m_block )
return m_block - > hit_test_position ( offset ) ;
return { } ;
}
Subshell : : Subshell ( Position position , RefPtr < Node > block )
: Node ( move ( position ) )
, m_block ( block )
{
if ( m_block & & m_block - > is_syntax_error ( ) )
set_is_syntax_error ( m_block - > syntax_error_node ( ) ) ;
}
Subshell : : ~ Subshell ( )
{
}
2021-03-13 02:40:18 +03:00
void Slice : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_selector - > dump ( level + 1 ) ;
}
RefPtr < Value > Slice : : run ( RefPtr < Shell > shell )
{
return m_selector - > run ( shell ) ;
}
void Slice : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
m_selector - > highlight_in_editor ( editor , shell , metadata ) ;
}
HitTestResult Slice : : hit_test_position ( size_t offset ) const
{
return m_selector - > hit_test_position ( offset ) ;
}
Vector < Line : : CompletionSuggestion > Slice : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
{
// TODO: Maybe intercept this, and suggest values in range?
return m_selector - > complete_for_editor ( shell , offset , hit_test_result ) ;
}
Slice : : Slice ( Position position , NonnullRefPtr < AST : : Node > selector )
: Node ( move ( position ) )
, m_selector ( move ( selector ) )
{
if ( m_selector - > is_syntax_error ( ) )
set_is_syntax_error ( m_selector - > syntax_error_node ( ) ) ;
}
Slice : : ~ Slice ( )
{
}
2020-06-17 16:35:06 +03:00
void SimpleVariable : : dump ( int level ) const
{
Node : : dump ( level ) ;
2021-03-13 02:40:18 +03:00
print_indented ( " (Name) " , level + 1 ) ;
print_indented ( m_name , level + 2 ) ;
print_indented ( " (Slice) " , level + 1 ) ;
if ( m_slice )
m_slice - > dump ( level + 2 ) ;
else
print_indented ( " (None) " , level + 2 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > SimpleVariable : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
2021-03-13 02:40:18 +03:00
NonnullRefPtr < Value > value = create < SimpleVariableValue > ( m_name ) ;
if ( m_slice )
value = value - > with_slices ( * m_slice ) ;
return value ;
2020-06-17 16:35:06 +03:00
}
2021-03-13 02:40:18 +03:00
void SimpleVariable : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
2020-06-17 16:35:06 +03:00
{
Line : : Style style { Line : : Style : : Foreground ( 214 , 112 , 214 ) } ;
if ( metadata . is_first_in_list )
style . unify_with ( { Line : : Style : : Bold } ) ;
editor . stylize ( { m_position . start_offset , m_position . end_offset } , move ( style ) ) ;
2021-03-13 02:40:18 +03:00
if ( m_slice )
m_slice - > highlight_in_editor ( editor , shell , metadata ) ;
2020-06-17 16:35:06 +03:00
}
2021-03-02 09:05:04 +03:00
HitTestResult SimpleVariable : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
2021-03-13 02:40:18 +03:00
if ( m_slice & & m_slice - > position ( ) . contains ( offset ) )
return m_slice - > hit_test_position ( offset ) ;
2020-06-29 04:56:06 +03:00
return { this , this , nullptr } ;
2020-06-17 16:35:06 +03:00
}
2020-06-29 04:56:06 +03:00
Vector < Line : : CompletionSuggestion > SimpleVariable : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
2020-06-17 16:35:06 +03:00
{
2020-06-29 04:56:06 +03:00
auto matching_node = hit_test_result . matching_node ;
2020-06-17 16:35:06 +03:00
if ( ! matching_node )
return { } ;
if ( matching_node ! = this )
return { } ;
auto corrected_offset = offset - matching_node - > position ( ) . start_offset - 1 ;
if ( corrected_offset > m_name . length ( ) + 1 )
return { } ;
return shell . complete_variable ( m_name , corrected_offset ) ;
}
SimpleVariable : : SimpleVariable ( Position position , String name )
2021-03-13 02:40:18 +03:00
: VariableNode ( move ( position ) )
2020-06-17 16:35:06 +03:00
, m_name ( move ( name ) )
{
}
SimpleVariable : : ~ SimpleVariable ( )
{
}
void SpecialVariable : : dump ( int level ) const
{
Node : : dump ( level ) ;
2021-03-13 02:40:18 +03:00
print_indented ( " (Name) " , level + 1 ) ;
2020-06-17 16:35:06 +03:00
print_indented ( String { & m_name , 1 } , level + 1 ) ;
2021-03-13 02:40:18 +03:00
print_indented ( " (Slice) " , level + 1 ) ;
if ( m_slice )
m_slice - > dump ( level + 2 ) ;
else
print_indented ( " (None) " , level + 2 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > SpecialVariable : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
2021-03-13 02:40:18 +03:00
NonnullRefPtr < Value > value = create < SpecialVariableValue > ( m_name ) ;
if ( m_slice )
value = value - > with_slices ( * m_slice ) ;
return value ;
2020-06-17 16:35:06 +03:00
}
2021-03-13 02:40:18 +03:00
void SpecialVariable : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
2020-06-17 16:35:06 +03:00
{
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( 214 , 112 , 214 ) } ) ;
2021-03-13 02:40:18 +03:00
if ( m_slice )
m_slice - > highlight_in_editor ( editor , shell , metadata ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-29 04:56:06 +03:00
Vector < Line : : CompletionSuggestion > SpecialVariable : : complete_for_editor ( Shell & , size_t , const HitTestResult & )
2020-06-17 16:35:06 +03:00
{
return { } ;
}
2021-03-02 09:05:04 +03:00
HitTestResult SpecialVariable : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
2021-03-13 02:40:18 +03:00
if ( m_slice & & m_slice - > position ( ) . contains ( offset ) )
return m_slice - > hit_test_position ( offset ) ;
2020-06-29 04:56:06 +03:00
return { this , this , nullptr } ;
2020-06-17 16:35:06 +03:00
}
SpecialVariable : : SpecialVariable ( Position position , char name )
2021-03-13 02:40:18 +03:00
: VariableNode ( move ( position ) )
2020-06-17 16:35:06 +03:00
, m_name ( name )
{
}
SpecialVariable : : ~ SpecialVariable ( )
{
}
2020-06-20 16:30:45 +03:00
void Juxtaposition : : dump ( int level ) const
2020-06-17 16:35:06 +03:00
{
Node : : dump ( level ) ;
m_left - > dump ( level + 1 ) ;
m_right - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Juxtaposition : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-06-23 17:40:41 +03:00
auto left_value = m_left - > run ( shell ) - > resolve_without_cast ( shell ) ;
auto right_value = m_right - > run ( shell ) - > resolve_without_cast ( shell ) ;
2020-06-20 16:30:45 +03:00
2020-06-23 17:40:41 +03:00
auto left = left_value - > resolve_as_list ( shell ) ;
auto right = right_value - > resolve_as_list ( shell ) ;
2020-06-20 16:30:45 +03:00
if ( left_value - > is_string ( ) & & right_value - > is_string ( ) ) {
2021-02-23 22:42:32 +03:00
VERIFY ( left . size ( ) = = 1 ) ;
VERIFY ( right . size ( ) = = 1 ) ;
2020-06-20 16:30:45 +03:00
StringBuilder builder ;
builder . append ( left [ 0 ] ) ;
builder . append ( right [ 0 ] ) ;
return create < StringValue > ( builder . to_string ( ) ) ;
}
// Otherwise, treat them as lists and create a list product.
if ( left . is_empty ( ) | | right . is_empty ( ) )
return create < ListValue > ( { } ) ;
Vector < String > result ;
result . ensure_capacity ( left . size ( ) * right . size ( ) ) ;
2020-06-17 16:35:06 +03:00
StringBuilder builder ;
2020-06-20 16:30:45 +03:00
for ( auto & left_element : left ) {
for ( auto & right_element : right ) {
builder . append ( left_element ) ;
builder . append ( right_element ) ;
result . append ( builder . to_string ( ) ) ;
builder . clear ( ) ;
}
}
2020-06-17 16:35:06 +03:00
2020-06-20 16:30:45 +03:00
return create < ListValue > ( move ( result ) ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-20 16:30:45 +03:00
void Juxtaposition : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
2020-06-17 16:35:06 +03:00
{
m_left - > highlight_in_editor ( editor , shell , metadata ) ;
2020-06-23 17:40:41 +03:00
// '~/foo/bar' is special, we have to actually resolve the tilde
// since that resolution is a pure operation, we can just go ahead
// and do it to get the value :)
if ( m_right - > is_bareword ( ) & & m_left - > is_tilde ( ) ) {
auto tilde_value = m_left - > run ( shell ) - > resolve_as_list ( shell ) [ 0 ] ;
auto bareword_value = m_right - > run ( shell ) - > resolve_as_list ( shell ) [ 0 ] ;
StringBuilder path_builder ;
path_builder . append ( tilde_value ) ;
path_builder . append ( " / " ) ;
path_builder . append ( bareword_value ) ;
auto path = path_builder . to_string ( ) ;
if ( Core : : File : : exists ( path ) ) {
auto realpath = shell . resolve_path ( path ) ;
auto url = URL : : create_with_file_protocol ( realpath ) ;
url . set_host ( shell . hostname ) ;
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Hyperlink ( url . to_string ( ) ) } ) ;
}
} else {
2020-06-17 16:35:06 +03:00
m_right - > highlight_in_editor ( editor , shell , metadata ) ;
2020-06-23 17:40:41 +03:00
}
}
2020-06-29 04:56:06 +03:00
Vector < Line : : CompletionSuggestion > Juxtaposition : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
2020-06-23 17:40:41 +03:00
{
2020-06-29 04:56:06 +03:00
auto matching_node = hit_test_result . matching_node ;
2020-06-23 17:40:41 +03:00
// '~/foo/bar' is special, we have to actually resolve the tilde
// then complete the bareword with that path prefix.
if ( m_right - > is_bareword ( ) & & m_left - > is_tilde ( ) ) {
auto tilde_value = m_left - > run ( shell ) - > resolve_as_list ( shell ) [ 0 ] ;
auto corrected_offset = offset - matching_node - > position ( ) . start_offset ;
auto * node = static_cast < BarewordLiteral * > ( matching_node . ptr ( ) ) ;
if ( corrected_offset > node - > text ( ) . length ( ) )
return { } ;
auto text = node - > text ( ) . substring ( 1 , node - > text ( ) . length ( ) - 1 ) ;
2021-04-20 17:17:38 +03:00
return shell . complete_path ( tilde_value , text , corrected_offset - 1 , Shell : : ExecutableOnly : : No ) ;
2020-06-23 17:40:41 +03:00
}
2020-06-29 04:56:06 +03:00
return Node : : complete_for_editor ( shell , offset , hit_test_result ) ;
2020-06-17 16:35:06 +03:00
}
2021-03-02 09:05:04 +03:00
HitTestResult Juxtaposition : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
auto result = m_left - > hit_test_position ( offset ) ;
2020-06-23 17:40:41 +03:00
if ( ! result . closest_node_with_semantic_meaning )
result . closest_node_with_semantic_meaning = this ;
2020-06-17 16:35:06 +03:00
if ( result . matching_node )
return result ;
2020-06-23 17:40:41 +03:00
result = m_right - > hit_test_position ( offset ) ;
if ( ! result . closest_node_with_semantic_meaning )
result . closest_node_with_semantic_meaning = this ;
return result ;
2020-06-17 16:35:06 +03:00
}
2020-09-16 03:37:14 +03:00
Juxtaposition : : Juxtaposition ( Position position , NonnullRefPtr < Node > left , NonnullRefPtr < Node > right )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_left ( move ( left ) )
, m_right ( move ( right ) )
{
2020-06-28 17:12:57 +03:00
if ( m_left - > is_syntax_error ( ) )
set_is_syntax_error ( m_left - > syntax_error_node ( ) ) ;
else if ( m_right - > is_syntax_error ( ) )
set_is_syntax_error ( m_right - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-20 16:30:45 +03:00
Juxtaposition : : ~ Juxtaposition ( )
2020-06-17 16:35:06 +03:00
{
}
void StringLiteral : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( m_text , level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > StringLiteral : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
2020-06-20 16:30:45 +03:00
return create < StringValue > ( m_text ) ;
2020-06-17 16:35:06 +03:00
}
void StringLiteral : : highlight_in_editor ( Line : : Editor & editor , Shell & , HighlightMetadata metadata )
{
2020-10-24 17:43:02 +03:00
if ( m_text . is_empty ( ) )
return ;
2020-06-17 16:35:06 +03:00
Line : : Style style { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Yellow ) } ;
if ( metadata . is_first_in_list )
style . unify_with ( { Line : : Style : : Bold } ) ;
editor . stylize ( { m_position . start_offset , m_position . end_offset } , move ( style ) ) ;
}
StringLiteral : : StringLiteral ( Position position , String text )
: Node ( move ( position ) )
, m_text ( move ( text ) )
{
}
StringLiteral : : ~ StringLiteral ( )
{
}
void StringPartCompose : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_left - > dump ( level + 1 ) ;
m_right - > dump ( level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > StringPartCompose : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-06-23 17:40:41 +03:00
auto left = m_left - > run ( shell ) - > resolve_as_list ( shell ) ;
auto right = m_right - > run ( shell ) - > resolve_as_list ( shell ) ;
2020-06-17 16:35:06 +03:00
StringBuilder builder ;
builder . join ( " " , left ) ;
builder . join ( " " , right ) ;
2020-06-20 16:30:45 +03:00
return create < StringValue > ( builder . to_string ( ) ) ;
2020-06-17 16:35:06 +03:00
}
void StringPartCompose : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
m_left - > highlight_in_editor ( editor , shell , metadata ) ;
m_right - > highlight_in_editor ( editor , shell , metadata ) ;
}
2021-03-02 09:05:04 +03:00
HitTestResult StringPartCompose : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
auto result = m_left - > hit_test_position ( offset ) ;
if ( result . matching_node )
return result ;
return m_right - > hit_test_position ( offset ) ;
}
2020-09-16 03:37:14 +03:00
StringPartCompose : : StringPartCompose ( Position position , NonnullRefPtr < Node > left , NonnullRefPtr < Node > right )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
, m_left ( move ( left ) )
, m_right ( move ( right ) )
{
2020-06-28 17:12:57 +03:00
if ( m_left - > is_syntax_error ( ) )
set_is_syntax_error ( m_left - > syntax_error_node ( ) ) ;
else if ( m_right - > is_syntax_error ( ) )
set_is_syntax_error ( m_right - > syntax_error_node ( ) ) ;
2020-06-17 16:35:06 +03:00
}
StringPartCompose : : ~ StringPartCompose ( )
{
}
void SyntaxError : : dump ( int level ) const
{
Node : : dump ( level ) ;
2020-12-01 12:25:14 +03:00
print_indented ( " (Error text) " , level + 1 ) ;
print_indented ( m_syntax_error_text , level + 2 ) ;
print_indented ( " (Can be recovered from) " , level + 1 ) ;
print_indented ( String : : formatted ( " {} " , m_is_continuable ) , level + 2 ) ;
2020-06-17 16:35:06 +03:00
}
2020-12-10 17:55:13 +03:00
RefPtr < Value > SyntaxError : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2021-01-03 12:08:20 +03:00
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , m_syntax_error_text , position ( ) ) ;
2020-06-20 16:30:45 +03:00
return create < StringValue > ( " " ) ;
2020-06-17 16:35:06 +03:00
}
void SyntaxError : : highlight_in_editor ( Line : : Editor & editor , Shell & , HighlightMetadata )
{
editor . stylize ( { m_position . start_offset , m_position . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Red ) , Line : : Style : : Bold } ) ;
}
2020-12-01 12:25:14 +03:00
SyntaxError : : SyntaxError ( Position position , String error , bool is_continuable )
2020-06-17 16:35:06 +03:00
: Node ( move ( position ) )
2020-06-28 17:12:57 +03:00
, m_syntax_error_text ( move ( error ) )
2020-12-01 12:25:14 +03:00
, m_is_continuable ( is_continuable )
2020-06-17 16:35:06 +03:00
{
2020-06-28 17:12:57 +03:00
}
const SyntaxError & SyntaxError : : syntax_error_node ( ) const
{
return * this ;
2020-06-17 16:35:06 +03:00
}
SyntaxError : : ~ SyntaxError ( )
{
}
2021-03-05 16:03:23 +03:00
void SyntheticNode : : dump ( int level ) const
{
Node : : dump ( level ) ;
}
RefPtr < Value > SyntheticNode : : run ( RefPtr < Shell > )
{
return m_value ;
}
void SyntheticNode : : highlight_in_editor ( Line : : Editor & , Shell & , HighlightMetadata )
{
}
SyntheticNode : : SyntheticNode ( Position position , NonnullRefPtr < Value > value )
: Node ( move ( position ) )
, m_value ( move ( value ) )
{
}
2020-06-17 16:35:06 +03:00
void Tilde : : dump ( int level ) const
{
Node : : dump ( level ) ;
print_indented ( m_username , level + 1 ) ;
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > Tilde : : run ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
2020-06-20 16:30:45 +03:00
return create < TildeValue > ( m_username ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
void Tilde : : highlight_in_editor ( Line : : Editor & , Shell & , HighlightMetadata )
2020-06-17 16:35:06 +03:00
{
}
2021-03-02 09:05:04 +03:00
HitTestResult Tilde : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
if ( ! position ( ) . contains ( offset ) )
return { } ;
2020-06-29 04:56:06 +03:00
return { this , this , nullptr } ;
2020-06-17 16:35:06 +03:00
}
2020-06-29 04:56:06 +03:00
Vector < Line : : CompletionSuggestion > Tilde : : complete_for_editor ( Shell & shell , size_t offset , const HitTestResult & hit_test_result )
2020-06-17 16:35:06 +03:00
{
2020-06-29 04:56:06 +03:00
auto matching_node = hit_test_result . matching_node ;
2020-06-23 17:40:41 +03:00
if ( ! matching_node )
return { } ;
if ( matching_node ! = this )
return { } ;
auto corrected_offset = offset - matching_node - > position ( ) . start_offset - 1 ;
if ( corrected_offset > m_username . length ( ) + 1 )
return { } ;
return shell . complete_user ( m_username , corrected_offset ) ;
2020-06-17 16:35:06 +03:00
}
String Tilde : : text ( ) const
{
StringBuilder builder ;
builder . append ( ' ~ ' ) ;
builder . append ( m_username ) ;
return builder . to_string ( ) ;
}
Tilde : : Tilde ( Position position , String username )
: Node ( move ( position ) )
, m_username ( move ( username ) )
{
}
Tilde : : ~ Tilde ( )
{
}
void WriteAppendRedirection : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_path - > dump ( level + 1 ) ;
2021-04-21 23:16:14 +03:00
print_indented ( String : : formatted ( " From {} " , m_fd ) , level + 1 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > WriteAppendRedirection : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
Command command ;
2020-06-23 17:40:41 +03:00
auto path_segments = m_path - > run ( shell ) - > resolve_as_list ( shell ) ;
2020-06-17 16:35:06 +03:00
StringBuilder builder ;
builder . join ( " " , path_segments ) ;
2020-08-12 13:15:30 +03:00
command . redirections . append ( PathRedirection : : create ( builder . to_string ( ) , m_fd , PathRedirection : : WriteAppend ) ) ;
2020-06-20 16:30:45 +03:00
return create < CommandValue > ( move ( command ) ) ;
2020-06-17 16:35:06 +03:00
}
2020-09-16 03:37:14 +03:00
WriteAppendRedirection : : WriteAppendRedirection ( Position position , int fd , NonnullRefPtr < Node > path )
2020-06-17 16:35:06 +03:00
: PathRedirectionNode ( move ( position ) , fd , move ( path ) )
{
}
WriteAppendRedirection : : ~ WriteAppendRedirection ( )
{
}
void WriteRedirection : : dump ( int level ) const
{
Node : : dump ( level ) ;
m_path - > dump ( level + 1 ) ;
2021-04-21 23:16:14 +03:00
print_indented ( String : : formatted ( " From {} " , m_fd ) , level + 1 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > WriteRedirection : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
Command command ;
2020-06-23 17:40:41 +03:00
auto path_segments = m_path - > run ( shell ) - > resolve_as_list ( shell ) ;
2020-06-17 16:35:06 +03:00
StringBuilder builder ;
builder . join ( " " , path_segments ) ;
2020-08-12 13:15:30 +03:00
command . redirections . append ( PathRedirection : : create ( builder . to_string ( ) , m_fd , PathRedirection : : Write ) ) ;
2020-06-20 16:30:45 +03:00
return create < CommandValue > ( move ( command ) ) ;
2020-06-17 16:35:06 +03:00
}
2020-09-16 03:37:14 +03:00
WriteRedirection : : WriteRedirection ( Position position , int fd , NonnullRefPtr < Node > path )
2020-06-17 16:35:06 +03:00
: PathRedirectionNode ( move ( position ) , fd , move ( path ) )
{
}
WriteRedirection : : ~ WriteRedirection ( )
{
}
void VariableDeclarations : : dump ( int level ) const
{
Node : : dump ( level ) ;
for ( auto & var : m_variables ) {
print_indented ( " Set " , level + 1 ) ;
var . name - > dump ( level + 2 ) ;
var . value - > dump ( level + 2 ) ;
}
}
2020-06-23 17:40:41 +03:00
RefPtr < Value > VariableDeclarations : : run ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
for ( auto & var : m_variables ) {
2020-06-23 17:40:41 +03:00
auto name_value = var . name - > run ( shell ) - > resolve_as_list ( shell ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( name_value . size ( ) = = 1 ) ;
2020-06-17 16:35:06 +03:00
auto name = name_value [ 0 ] ;
2020-06-23 17:40:41 +03:00
auto value = var . value - > run ( shell ) ;
2021-01-18 04:06:19 +03:00
shell - > set_local_variable ( name , value . release_nonnull ( ) ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-20 16:30:45 +03:00
return create < ListValue > ( { } ) ;
2020-06-17 16:35:06 +03:00
}
void VariableDeclarations : : highlight_in_editor ( Line : : Editor & editor , Shell & shell , HighlightMetadata metadata )
{
metadata . is_first_in_list = false ;
for ( auto & var : m_variables ) {
var . name - > highlight_in_editor ( editor , shell , metadata ) ;
// Highlight the '='.
editor . stylize ( { var . name - > position ( ) . end_offset - 1 , var . name - > position ( ) . end_offset } , { Line : : Style : : Foreground ( Line : : Style : : XtermColor : : Blue ) } ) ;
var . value - > highlight_in_editor ( editor , shell , metadata ) ;
}
}
2021-03-02 09:05:04 +03:00
HitTestResult VariableDeclarations : : hit_test_position ( size_t offset ) const
2020-06-17 16:35:06 +03:00
{
for ( auto decl : m_variables ) {
auto result = decl . value - > hit_test_position ( offset ) ;
if ( result . matching_node )
return result ;
}
2020-06-29 04:56:06 +03:00
return { nullptr , nullptr , nullptr } ;
2020-06-17 16:35:06 +03:00
}
VariableDeclarations : : VariableDeclarations ( Position position , Vector < Variable > variables )
: Node ( move ( position ) )
, m_variables ( move ( variables ) )
{
2020-06-23 17:40:41 +03:00
for ( auto & decl : m_variables ) {
2020-06-28 17:12:57 +03:00
if ( decl . name - > is_syntax_error ( ) ) {
set_is_syntax_error ( decl . name - > syntax_error_node ( ) ) ;
break ;
}
if ( decl . value - > is_syntax_error ( ) ) {
set_is_syntax_error ( decl . value - > syntax_error_node ( ) ) ;
2020-06-23 17:40:41 +03:00
break ;
}
}
2020-06-17 16:35:06 +03:00
}
VariableDeclarations : : ~ VariableDeclarations ( )
{
}
Value : : ~ Value ( )
{
}
2020-06-23 17:40:41 +03:00
Vector < AST : : Command > Value : : resolve_as_commands ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
Command command ;
2020-06-23 17:40:41 +03:00
command . argv = resolve_as_list ( shell ) ;
2020-06-17 16:35:06 +03:00
return { command } ;
}
ListValue : : ListValue ( Vector < String > values )
{
2021-03-05 16:03:23 +03:00
if ( values . is_empty ( ) )
return ;
2020-06-17 16:35:06 +03:00
m_contained_values . ensure_capacity ( values . size ( ) ) ;
for ( auto & str : values )
2021-04-23 17:46:57 +03:00
m_contained_values . append ( adopt_ref ( * new StringValue ( move ( str ) ) ) ) ;
2020-06-17 16:35:06 +03:00
}
2021-03-13 02:40:18 +03:00
NonnullRefPtr < Value > Value : : with_slices ( NonnullRefPtr < Slice > slice ) const &
{
auto value = clone ( ) ;
value - > m_slices . append ( move ( slice ) ) ;
return value ;
}
NonnullRefPtr < Value > Value : : with_slices ( NonnullRefPtrVector < Slice > slices ) const &
{
auto value = clone ( ) ;
2021-06-12 14:24:45 +03:00
value - > m_slices . extend ( move ( slices ) ) ;
2021-03-13 02:40:18 +03:00
return value ;
}
2020-06-20 16:30:45 +03:00
ListValue : : ~ ListValue ( )
{
}
2020-06-23 17:40:41 +03:00
Vector < String > ListValue : : resolve_as_list ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
Vector < String > values ;
for ( auto & value : m_contained_values )
2021-06-12 14:24:45 +03:00
values . extend ( value . resolve_as_list ( shell ) ) ;
2020-06-17 16:35:06 +03:00
2021-03-13 02:40:18 +03:00
return resolve_slices ( shell , move ( values ) , m_slices ) ;
2020-06-17 16:35:06 +03:00
}
2020-08-07 10:36:15 +03:00
NonnullRefPtr < Value > ListValue : : resolve_without_cast ( RefPtr < Shell > shell )
2020-07-12 00:11:24 +03:00
{
2020-08-07 10:33:05 +03:00
NonnullRefPtrVector < Value > values ;
2020-07-12 00:11:24 +03:00
for ( auto & value : m_contained_values )
2020-08-07 10:36:15 +03:00
values . append ( value . resolve_without_cast ( shell ) ) ;
2020-07-12 00:11:24 +03:00
2021-03-13 02:40:18 +03:00
NonnullRefPtr < Value > value = create < ListValue > ( move ( values ) ) ;
if ( ! m_slices . is_empty ( ) )
value = value - > with_slices ( m_slices ) ;
return value ;
2020-07-12 00:11:24 +03:00
}
2020-06-17 16:35:06 +03:00
CommandValue : : ~ CommandValue ( )
{
}
CommandSequenceValue : : ~ CommandSequenceValue ( )
{
}
2021-03-13 02:40:18 +03:00
Vector < String > CommandSequenceValue : : resolve_as_list ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2021-03-13 02:40:18 +03:00
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " Unexpected cast of a command sequence to a list " ) ;
2020-06-17 16:35:06 +03:00
return { } ;
}
2020-06-23 17:40:41 +03:00
Vector < Command > CommandSequenceValue : : resolve_as_commands ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
return m_contained_values ;
}
2021-03-13 02:40:18 +03:00
Vector < String > CommandValue : : resolve_as_list ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2021-03-13 02:40:18 +03:00
shell - > raise_error ( Shell : : ShellError : : EvaluatedSyntaxError , " Unexpected cast of a command to a list " ) ;
2020-06-17 16:35:06 +03:00
return { } ;
}
2020-06-23 17:40:41 +03:00
Vector < Command > CommandValue : : resolve_as_commands ( RefPtr < Shell > )
2020-06-17 16:35:06 +03:00
{
return { m_command } ;
}
JobValue : : ~ JobValue ( )
{
}
StringValue : : ~ StringValue ( )
{
}
2021-03-13 02:40:18 +03:00
Vector < String > StringValue : : resolve_as_list ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
if ( is_list ( ) ) {
2020-06-29 04:52:58 +03:00
auto parts = StringView ( m_string ) . split_view ( m_split , m_keep_empty ) ;
2020-06-17 16:35:06 +03:00
Vector < String > result ;
result . ensure_capacity ( parts . size ( ) ) ;
for ( auto & part : parts )
result . append ( part ) ;
2021-03-13 02:40:18 +03:00
return resolve_slices ( shell , move ( result ) , m_slices ) ;
2020-06-17 16:35:06 +03:00
}
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , String { m_string } , m_slices ) } ;
2020-06-17 16:35:06 +03:00
}
2021-03-05 16:03:23 +03:00
NonnullRefPtr < Value > StringValue : : resolve_without_cast ( RefPtr < Shell > shell )
{
if ( is_list ( ) )
2021-03-13 02:40:18 +03:00
return create < AST : : ListValue > ( resolve_as_list ( shell ) ) ; // No need to reapply the slices.
2021-03-05 16:03:23 +03:00
return * this ;
}
2020-06-17 16:35:06 +03:00
GlobValue : : ~ GlobValue ( )
{
}
2020-06-23 17:40:41 +03:00
Vector < String > GlobValue : : resolve_as_list ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-09-14 18:00:20 +03:00
if ( ! shell )
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , String { m_glob } , m_slices ) } ;
2020-09-14 18:00:20 +03:00
2020-12-10 17:55:13 +03:00
auto results = shell - > expand_globs ( m_glob , shell - > cwd ) ;
if ( results . is_empty ( ) )
2021-01-03 12:08:20 +03:00
shell - > raise_error ( Shell : : ShellError : : InvalidGlobError , " Glob did not match anything! " , m_generation_position ) ;
2021-03-13 02:40:18 +03:00
return resolve_slices ( shell , move ( results ) , m_slices ) ;
2020-06-17 16:35:06 +03:00
}
SimpleVariableValue : : ~ SimpleVariableValue ( )
{
}
2020-06-23 17:40:41 +03:00
Vector < String > SimpleVariableValue : : resolve_as_list ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-09-14 18:00:20 +03:00
if ( ! shell )
2021-03-13 02:40:18 +03:00
return resolve_slices ( shell , Vector < String > { } , m_slices ) ;
2020-09-14 18:00:20 +03:00
2020-06-23 17:40:41 +03:00
if ( auto value = resolve_without_cast ( shell ) ; value ! = this )
return value - > resolve_as_list ( shell ) ;
2020-06-17 16:35:06 +03:00
char * env_value = getenv ( m_name . characters ( ) ) ;
if ( env_value = = nullptr )
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , " " , m_slices ) } ;
2020-06-17 16:35:06 +03:00
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , String { env_value } , m_slices ) } ;
2020-06-17 16:35:06 +03:00
}
2020-08-07 10:36:15 +03:00
NonnullRefPtr < Value > SimpleVariableValue : : resolve_without_cast ( RefPtr < Shell > shell )
2020-06-22 14:07:20 +03:00
{
2021-02-23 22:42:32 +03:00
VERIFY ( shell ) ;
2020-09-14 18:00:20 +03:00
2021-03-13 02:40:18 +03:00
if ( auto value = shell - > lookup_local_variable ( m_name ) ) {
auto result = value . release_nonnull ( ) ;
// If a slice is applied, add it.
if ( ! m_slices . is_empty ( ) )
result = result - > with_slices ( m_slices ) ;
return result ;
}
2020-08-07 10:36:15 +03:00
return * this ;
2020-06-22 14:07:20 +03:00
}
2020-06-17 16:35:06 +03:00
SpecialVariableValue : : ~ SpecialVariableValue ( )
{
}
2020-08-04 07:57:25 +03:00
2020-06-23 17:40:41 +03:00
Vector < String > SpecialVariableValue : : resolve_as_list ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
2020-09-14 18:00:20 +03:00
if ( ! shell )
return { } ;
2020-06-17 16:35:06 +03:00
switch ( m_name ) {
case ' ? ' :
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , String : : number ( shell - > last_return_code ) , m_slices ) } ;
2020-06-17 16:35:06 +03:00
case ' $ ' :
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , String : : number ( getpid ( ) ) , m_slices ) } ;
2020-08-04 07:57:25 +03:00
case ' * ' :
if ( auto argv = shell - > lookup_local_variable ( " ARGV " ) )
2021-03-13 02:40:18 +03:00
return resolve_slices ( shell , argv - > resolve_as_list ( shell ) , m_slices ) ;
return resolve_slices ( shell , Vector < String > { } , m_slices ) ;
2020-08-04 07:57:25 +03:00
case ' # ' :
if ( auto argv = shell - > lookup_local_variable ( " ARGV " ) ) {
if ( argv - > is_list ( ) ) {
auto list_argv = static_cast < AST : : ListValue * > ( argv . ptr ( ) ) ;
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , String : : number ( list_argv - > values ( ) . size ( ) ) , m_slices ) } ;
2020-08-04 07:57:25 +03:00
}
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , " 1 " , m_slices ) } ;
2020-08-04 07:57:25 +03:00
}
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , " 0 " , m_slices ) } ;
2020-06-17 16:35:06 +03:00
default :
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , " " , m_slices ) } ;
2020-06-17 16:35:06 +03:00
}
}
TildeValue : : ~ TildeValue ( )
{
}
2020-06-23 17:40:41 +03:00
Vector < String > TildeValue : : resolve_as_list ( RefPtr < Shell > shell )
2020-06-17 16:35:06 +03:00
{
StringBuilder builder ;
builder . append ( " ~ " ) ;
builder . append ( m_username ) ;
2020-09-14 18:00:20 +03:00
if ( ! shell )
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , builder . to_string ( ) , m_slices ) } ;
2020-09-14 18:00:20 +03:00
2021-03-13 02:40:18 +03:00
return { resolve_slices ( shell , shell - > expand_tilde ( builder . to_string ( ) ) , m_slices ) } ;
2020-06-17 16:35:06 +03:00
}
2020-08-04 19:16:37 +03:00
Result < NonnullRefPtr < Rewiring > , String > CloseRedirection : : apply ( ) const
2020-06-17 16:35:06 +03:00
{
2021-04-23 17:46:57 +03:00
return adopt_ref ( * new Rewiring ( fd , fd , Rewiring : : Close : : ImmediatelyCloseNew ) ) ;
2020-06-17 16:35:06 +03:00
}
CloseRedirection : : ~ CloseRedirection ( )
{
}
2020-08-04 19:16:37 +03:00
Result < NonnullRefPtr < Rewiring > , String > PathRedirection : : apply ( ) const
2020-06-17 16:35:06 +03:00
{
2020-08-04 19:16:37 +03:00
auto check_fd_and_return = [ my_fd = this - > fd ] ( int fd , const String & path ) - > Result < NonnullRefPtr < Rewiring > , String > {
2020-06-17 16:35:06 +03:00
if ( fd < 0 ) {
String error = strerror ( errno ) ;
2020-12-06 20:21:40 +03:00
dbgln ( " open() failed for '{}' with {} " , path , error ) ;
2020-06-17 16:35:06 +03:00
return error ;
}
2021-04-23 17:46:57 +03:00
return adopt_ref ( * new Rewiring ( fd , my_fd , Rewiring : : Close : : Old ) ) ;
2020-06-17 16:35:06 +03:00
} ;
switch ( direction ) {
case AST : : PathRedirection : : WriteAppend :
return check_fd_and_return ( open ( path . characters ( ) , O_WRONLY | O_CREAT | O_APPEND , 0666 ) , path ) ;
case AST : : PathRedirection : : Write :
return check_fd_and_return ( open ( path . characters ( ) , O_WRONLY | O_CREAT | O_TRUNC , 0666 ) , path ) ;
case AST : : PathRedirection : : Read :
return check_fd_and_return ( open ( path . characters ( ) , O_RDONLY ) , path ) ;
case AST : : PathRedirection : : ReadWrite :
return check_fd_and_return ( open ( path . characters ( ) , O_RDWR | O_CREAT , 0666 ) , path ) ;
}
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-06-17 16:35:06 +03:00
}
PathRedirection : : ~ PathRedirection ( )
{
}
FdRedirection : : ~ FdRedirection ( )
{
}
Redirection : : ~ Redirection ( )
{
}
}