diff --git a/Shell/AST.cpp b/Shell/AST.cpp index eac2501ee46..9fd98a74fdb 100644 --- a/Shell/AST.cpp +++ b/Shell/AST.cpp @@ -96,7 +96,7 @@ Vector Node::complete_for_editor(Shell& shell, size_ if (corrected_offset > node->text().length()) return {}; - return shell.complete_path(node->text(), corrected_offset); + return shell.complete_path("", node->text(), corrected_offset); } return {}; } @@ -129,11 +129,9 @@ void And::dump(int level) const m_right->dump(level + 1); } -RefPtr And::run(TheExecutionInputType input_value) +RefPtr And::run(RefPtr shell) { - auto shell = input_value; - - auto left = m_left->run(input_value); + auto left = m_left->run(shell); ASSERT(left->is_job()); auto* job_value = static_cast(left.ptr()); @@ -146,7 +144,7 @@ RefPtr And::run(TheExecutionInputType input_value) shell->block_on_job(job); if (job->exit_code() == 0) - return m_right->run(input_value); + return m_right->run(shell); return job_value; } @@ -174,6 +172,8 @@ And::And(Position position, RefPtr left, RefPtr right) , m_left(move(left)) , m_right(move(right)) { + if (m_left->is_syntax_error() || m_right->is_syntax_error()) + set_is_syntax_error(); } And::~And() @@ -187,13 +187,13 @@ void ListConcatenate::dump(int level) const m_list->dump(level + 1); } -RefPtr ListConcatenate::run(TheExecutionInputType input_value) +RefPtr ListConcatenate::run(RefPtr shell) { - auto list = m_list->run(input_value)->resolve_without_cast(input_value); - auto element = m_element->run(input_value)->resolve_without_cast(input_value); + auto list = m_list->run(shell)->resolve_without_cast(shell); + auto element = m_element->run(shell)->resolve_without_cast(shell); if (list->is_command() || element->is_command()) { - auto joined_commands = join_commands(element->resolve_as_commands(input_value), list->resolve_as_commands(input_value)); + auto joined_commands = join_commands(element->resolve_as_commands(shell), list->resolve_as_commands(shell)); if (joined_commands.size() == 1) return create(joined_commands[0]); @@ -231,6 +231,8 @@ ListConcatenate::ListConcatenate(Position position, RefPtr element, RefPtr , m_element(move(element)) , m_list(move(list)) { + if (m_element->is_syntax_error() || m_list->is_syntax_error()) + set_is_syntax_error(); } ListConcatenate::~ListConcatenate() @@ -243,9 +245,9 @@ void Background::dump(int level) const m_command->dump(level + 1); } -RefPtr Background::run(TheExecutionInputType input_value) +RefPtr Background::run(RefPtr shell) { - auto commands = m_command->run(input_value)->resolve_as_commands(input_value); + auto commands = m_command->run(shell)->resolve_as_commands(shell); auto& last = commands.last(); last.should_wait = false; @@ -269,6 +271,8 @@ Background::Background(Position position, RefPtr command) : Node(move(position)) , m_command(move(command)) { + if (m_command->is_syntax_error()) + set_is_syntax_error(); } Background::~Background() @@ -281,7 +285,7 @@ void BarewordLiteral::dump(int level) const print_indented(m_text, level + 1); } -RefPtr BarewordLiteral::run(TheExecutionInputType) +RefPtr BarewordLiteral::run(RefPtr) { return create(m_text); } @@ -331,17 +335,16 @@ void CastToCommand::dump(int level) const m_inner->dump(level + 1); } -RefPtr CastToCommand::run(TheExecutionInputType input_value) +RefPtr CastToCommand::run(RefPtr shell) { if (m_inner->is_command()) - return m_inner->run(input_value); + return m_inner->run(shell); - auto shell = input_value; - auto value = m_inner->run(input_value)->resolve_without_cast(input_value); + auto value = m_inner->run(shell)->resolve_without_cast(shell); if (value->is_command()) return value; - auto argv = value->resolve_as_list(input_value); + auto argv = value->resolve_as_list(shell); return create(move(argv)); } @@ -380,6 +383,8 @@ CastToCommand::CastToCommand(Position position, RefPtr inner) : Node(move(position)) , m_inner(move(inner)) { + if (m_inner->is_syntax_error()) + set_is_syntax_error(); } CastToCommand::~CastToCommand() @@ -395,18 +400,17 @@ void CastToList::dump(int level) const print_indented("(empty)", level + 1); } -RefPtr CastToList::run(TheExecutionInputType input_value) +RefPtr CastToList::run(RefPtr shell) { if (!m_inner) return create({}); - auto shell = input_value; - auto inner_value = m_inner->run(input_value); + auto inner_value = m_inner->run(shell); if (inner_value->is_command()) return inner_value; - auto values = inner_value->resolve_as_list(input_value); + auto values = inner_value->resolve_as_list(shell); Vector> cast_values; for (auto& value : values) cast_values.append(create(value)); @@ -435,6 +439,8 @@ CastToList::CastToList(Position position, RefPtr inner) : Node(move(position)) , m_inner(move(inner)) { + if (m_inner && m_inner->is_syntax_error()) + set_is_syntax_error(); } CastToList::~CastToList() @@ -447,7 +453,7 @@ void CloseFdRedirection::dump(int level) const print_indented(String::format("%d -> Close", m_fd), level); } -RefPtr CloseFdRedirection::run(TheExecutionInputType) +RefPtr CloseFdRedirection::run(RefPtr) { Command command; command.redirections.append(*new CloseRedirection(m_fd)); @@ -476,7 +482,7 @@ void CommandLiteral::dump(int level) const print_indented("(Generated command literal)", level + 1); } -RefPtr CommandLiteral::run(TheExecutionInputType) +RefPtr CommandLiteral::run(RefPtr) { return create(m_command); } @@ -497,7 +503,7 @@ void Comment::dump(int level) const print_indented(m_text, level + 1); } -RefPtr Comment::run(TheExecutionInputType) +RefPtr Comment::run(RefPtr) { return create(""); } @@ -523,11 +529,10 @@ void DoubleQuotedString::dump(int level) const m_inner->dump(level + 1); } -RefPtr DoubleQuotedString::run(TheExecutionInputType input_value) +RefPtr DoubleQuotedString::run(RefPtr shell) { StringBuilder builder; - auto shell = input_value; - auto values = m_inner->run(input_value)->resolve_as_list(input_value); + auto values = m_inner->run(shell)->resolve_as_list(shell); builder.join("", values); @@ -557,6 +562,8 @@ DoubleQuotedString::DoubleQuotedString(Position position, RefPtr inner) : Node(move(position)) , m_inner(move(inner)) { + if (m_inner->is_syntax_error()) + set_is_syntax_error(); } DoubleQuotedString::~DoubleQuotedString() @@ -569,19 +576,19 @@ void DynamicEvaluate::dump(int level) const m_inner->dump(level + 1); } -RefPtr DynamicEvaluate::run(TheExecutionInputType input_value) +RefPtr DynamicEvaluate::run(RefPtr shell) { - auto result = m_inner->run(input_value)->resolve_without_cast(input_value); + auto result = m_inner->run(shell)->resolve_without_cast(shell); // Dynamic Evaluation behaves differently between strings and lists. // Strings are treated as variables, and Lists are treated as commands. if (result->is_string()) { - auto name_part = result->resolve_as_list(input_value); + auto name_part = result->resolve_as_list(shell); ASSERT(name_part.size() == 1); return create(name_part[0]); } // If it's anything else, we're just gonna cast it to a list. - auto list = result->resolve_as_list(input_value); + auto list = result->resolve_as_list(shell); return create(move(list)); } @@ -603,6 +610,8 @@ DynamicEvaluate::DynamicEvaluate(Position position, RefPtr inner) : Node(move(position)) , m_inner(move(inner)) { + if (m_inner->is_syntax_error()) + set_is_syntax_error(); } DynamicEvaluate::~DynamicEvaluate() @@ -615,7 +624,7 @@ void Fd2FdRedirection::dump(int level) const print_indented(String::format("%d -> %d", source_fd, dest_fd), level); } -RefPtr Fd2FdRedirection::run(TheExecutionInputType) +RefPtr Fd2FdRedirection::run(RefPtr) { Command command; command.redirections.append(*new FdRedirection(source_fd, dest_fd, Rewiring::Close::None)); @@ -644,7 +653,7 @@ void Glob::dump(int level) const print_indented(m_text, level + 1); } -RefPtr Glob::run(TheExecutionInputType) +RefPtr Glob::run(RefPtr) { return create(m_text); } @@ -675,12 +684,11 @@ void Execute::dump(int level) const m_command->dump(level + 1); } -RefPtr Execute::run(TheExecutionInputType input_value) +RefPtr Execute::run(RefPtr shell) { RefPtr job; - auto shell = input_value; - auto initial_commands = m_command->run(input_value)->resolve_as_commands(input_value); + auto initial_commands = m_command->run(shell)->resolve_as_commands(shell); decltype(initial_commands) commands; for (auto& command : initial_commands) { @@ -695,7 +703,7 @@ RefPtr Execute::run(TheExecutionInputType input_value) subcommand_ast = ast->command(); } RefPtr substitute = create(position(), move(subcommand_ast), create(position(), command)); - commands.append(substitute->run(input_value)->resolve_as_commands(input_value)); + commands.append(substitute->run(shell)->resolve_as_commands(shell)); } else { commands.append(command); } @@ -851,6 +859,8 @@ Execute::Execute(Position position, RefPtr command, bool capture_stdout) , m_command(move(command)) , m_capture_stdout(capture_stdout) { + if (m_command->is_syntax_error()) + set_is_syntax_error(); } Execute::~Execute() @@ -864,10 +874,10 @@ void Join::dump(int level) const m_right->dump(level + 1); } -RefPtr Join::run(TheExecutionInputType input_value) +RefPtr Join::run(RefPtr shell) { - auto left = m_left->run(input_value)->resolve_as_commands(input_value); - auto right = m_right->run(input_value)->resolve_as_commands(input_value); + auto left = m_left->run(shell)->resolve_as_commands(shell); + auto right = m_right->run(shell)->resolve_as_commands(shell); return create(join_commands(move(left), move(right))); } @@ -896,6 +906,8 @@ Join::Join(Position position, RefPtr left, RefPtr right) , m_left(move(left)) , m_right(move(right)) { + if (m_left->is_syntax_error() || m_right->is_syntax_error()) + set_is_syntax_error(); } Join::~Join() @@ -909,18 +921,17 @@ void Or::dump(int level) const m_right->dump(level + 1); } -RefPtr Or::run(TheExecutionInputType input_value) +RefPtr Or::run(RefPtr shell) { - auto shell = input_value; - auto left = m_left->run(input_value); + auto left = m_left->run(shell); ASSERT(left->is_job()); auto* job_value = static_cast(left.ptr()); const auto job = job_value->job(); if (!job) { // Something has gone wrong, let's just pretend that the job failed. - return m_right->run(input_value); + return m_right->run(shell); } shell->block_on_job(job); @@ -928,7 +939,7 @@ RefPtr Or::run(TheExecutionInputType input_value) if (job->exit_code() == 0) return job_value; - return m_right->run(input_value); + return m_right->run(shell); } void Or::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -953,6 +964,8 @@ Or::Or(Position position, RefPtr left, RefPtr right) , m_left(move(left)) , m_right(move(right)) { + if (m_left->is_syntax_error() || m_right->is_syntax_error()) + set_is_syntax_error(); } Or::~Or() @@ -966,10 +979,10 @@ void Pipe::dump(int level) const m_right->dump(level + 1); } -RefPtr Pipe::run(TheExecutionInputType input_value) +RefPtr Pipe::run(RefPtr shell) { - auto left = m_left->run(input_value)->resolve_as_commands(input_value); - auto right = m_right->run(input_value)->resolve_as_commands(input_value); + auto left = m_left->run(shell)->resolve_as_commands(shell); + auto right = m_right->run(shell)->resolve_as_commands(shell); auto last_in_left = left.take_last(); auto first_in_right = right.take_first(); @@ -1012,6 +1025,8 @@ Pipe::Pipe(Position position, RefPtr left, RefPtr right) , m_left(move(left)) , m_right(move(right)) { + if (m_left->is_syntax_error() || m_right->is_syntax_error()) + set_is_syntax_error(); } Pipe::~Pipe() @@ -1067,7 +1082,7 @@ Vector PathRedirectionNode::complete_for_editor(Shel if (corrected_offset > node->text().length()) return {}; - return shell.complete_path(node->text(), corrected_offset); + return shell.complete_path("", node->text(), corrected_offset); } PathRedirectionNode::~PathRedirectionNode() @@ -1081,10 +1096,10 @@ void ReadRedirection::dump(int level) const print_indented(String::format("To %d", m_fd), level + 1); } -RefPtr ReadRedirection::run(TheExecutionInputType input_value) +RefPtr ReadRedirection::run(RefPtr shell) { Command command; - auto path_segments = m_path->run(input_value)->resolve_as_list(input_value); + auto path_segments = m_path->run(shell)->resolve_as_list(shell); StringBuilder builder; builder.join(" ", path_segments); @@ -1108,10 +1123,10 @@ void ReadWriteRedirection::dump(int level) const print_indented(String::format("To/From %d", m_fd), level + 1); } -RefPtr ReadWriteRedirection::run(TheExecutionInputType input_value) +RefPtr ReadWriteRedirection::run(RefPtr shell) { Command command; - auto path_segments = m_path->run(input_value)->resolve_as_list(input_value); + auto path_segments = m_path->run(shell)->resolve_as_list(shell); StringBuilder builder; builder.join(" ", path_segments); @@ -1135,10 +1150,10 @@ void Sequence::dump(int level) const m_right->dump(level + 1); } -RefPtr Sequence::run(TheExecutionInputType input_value) +RefPtr Sequence::run(RefPtr shell) { - auto left = m_left->run(input_value)->resolve_as_commands(input_value); - auto right = m_right->run(input_value)->resolve_as_commands(input_value); + auto left = m_left->run(shell)->resolve_as_commands(shell); + auto right = m_right->run(shell)->resolve_as_commands(shell); Vector commands; commands.append(left); @@ -1169,6 +1184,8 @@ Sequence::Sequence(Position position, RefPtr left, RefPtr right) , m_left(move(left)) , m_right(move(right)) { + if (m_left->is_syntax_error() || m_right->is_syntax_error()) + set_is_syntax_error(); } Sequence::~Sequence() @@ -1181,7 +1198,7 @@ void SimpleVariable::dump(int level) const print_indented(m_name, level + 1); } -RefPtr SimpleVariable::run(TheExecutionInputType) +RefPtr SimpleVariable::run(RefPtr) { return create(m_name); } @@ -1234,7 +1251,7 @@ void SpecialVariable::dump(int level) const print_indented(String { &m_name, 1 }, level + 1); } -RefPtr SpecialVariable::run(TheExecutionInputType) +RefPtr SpecialVariable::run(RefPtr) { return create(m_name); } @@ -1274,13 +1291,13 @@ void Juxtaposition::dump(int level) const m_right->dump(level + 1); } -RefPtr Juxtaposition::run(TheExecutionInputType input_value) +RefPtr Juxtaposition::run(RefPtr shell) { - auto left_value = m_left->run(input_value)->resolve_without_cast(input_value); - auto right_value = m_right->run(input_value)->resolve_without_cast(input_value); + auto left_value = m_left->run(shell)->resolve_without_cast(shell); + auto right_value = m_right->run(shell)->resolve_without_cast(shell); - auto left = left_value->resolve_as_list(input_value); - auto right = right_value->resolve_as_list(input_value); + auto left = left_value->resolve_as_list(shell); + auto right = right_value->resolve_as_list(shell); if (left_value->is_string() && right_value->is_string()) { @@ -1318,9 +1335,50 @@ void Juxtaposition::highlight_in_editor(Line::Editor& editor, Shell& shell, High { m_left->highlight_in_editor(editor, shell, metadata); - // Do not highlight '/foo/bar' in '~/foo/bar' - if (!(m_right->is_bareword() && m_left->is_tilde())) + // '~/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 { m_right->highlight_in_editor(editor, shell, metadata); + } +} + +Vector Juxtaposition::complete_for_editor(Shell& shell, size_t offset, RefPtr matching_node) +{ + // '~/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(matching_node.ptr()); + + if (corrected_offset > node->text().length()) + return {}; + + auto text = node->text().substring(1, node->text().length() - 1); + + return shell.complete_path(tilde_value, text, corrected_offset - 1); + } + + return Node::complete_for_editor(shell, offset, matching_node); } HitTestResult Juxtaposition::hit_test_position(size_t offset) @@ -1329,9 +1387,15 @@ HitTestResult Juxtaposition::hit_test_position(size_t offset) return {}; auto result = m_left->hit_test_position(offset); + if (!result.closest_node_with_semantic_meaning) + result.closest_node_with_semantic_meaning = this; if (result.matching_node) return result; - return m_right->hit_test_position(offset); + + result = m_right->hit_test_position(offset); + if (!result.closest_node_with_semantic_meaning) + result.closest_node_with_semantic_meaning = this; + return result; } Juxtaposition::Juxtaposition(Position position, RefPtr left, RefPtr right) @@ -1339,6 +1403,8 @@ Juxtaposition::Juxtaposition(Position position, RefPtr left, RefPtr , m_left(move(left)) , m_right(move(right)) { + if (m_left->is_syntax_error() || m_right->is_syntax_error()) + set_is_syntax_error(); } Juxtaposition::~Juxtaposition() @@ -1351,7 +1417,7 @@ void StringLiteral::dump(int level) const print_indented(m_text, level + 1); } -RefPtr StringLiteral::run(TheExecutionInputType) +RefPtr StringLiteral::run(RefPtr) { return create(m_text); } @@ -1381,10 +1447,10 @@ void StringPartCompose::dump(int level) const m_right->dump(level + 1); } -RefPtr StringPartCompose::run(TheExecutionInputType input_value) +RefPtr StringPartCompose::run(RefPtr shell) { - auto left = m_left->run(input_value)->resolve_as_list(input_value); - auto right = m_right->run(input_value)->resolve_as_list(input_value); + auto left = m_left->run(shell)->resolve_as_list(shell); + auto right = m_right->run(shell)->resolve_as_list(shell); StringBuilder builder; builder.join(" ", left); @@ -1415,6 +1481,8 @@ StringPartCompose::StringPartCompose(Position position, RefPtr left, RefPt , m_left(move(left)) , m_right(move(right)) { + if (m_left->is_syntax_error() || m_right->is_syntax_error()) + set_is_syntax_error(); } StringPartCompose::~StringPartCompose() @@ -1426,7 +1494,7 @@ void SyntaxError::dump(int level) const Node::dump(level); } -RefPtr SyntaxError::run(TheExecutionInputType) +RefPtr SyntaxError::run(RefPtr) { dbg() << "SYNTAX ERROR AAAA"; return create(""); @@ -1440,6 +1508,7 @@ void SyntaxError::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMet SyntaxError::SyntaxError(Position position) : Node(move(position)) { + set_is_syntax_error(); } SyntaxError::~SyntaxError() @@ -1452,14 +1521,13 @@ void Tilde::dump(int level) const print_indented(m_username, level + 1); } -RefPtr Tilde::run(TheExecutionInputType) +RefPtr Tilde::run(RefPtr) { return create(m_username); } -void Tilde::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata) +void Tilde::highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata) { - editor.stylize({ m_position.start_offset, m_position.end_offset }, { Line::Style::Foreground(Line::Style::XtermColor::Cyan) }); } HitTestResult Tilde::hit_test_position(size_t offset) @@ -1470,9 +1538,20 @@ HitTestResult Tilde::hit_test_position(size_t offset) return { this, this }; } -Vector Tilde::complete_for_editor(Shell&, size_t, RefPtr) +Vector Tilde::complete_for_editor(Shell& shell, size_t offset, RefPtr matching_node) { - return {}; + 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); } String Tilde::text() const @@ -1500,10 +1579,10 @@ void WriteAppendRedirection::dump(int level) const print_indented(String::format("From %d", m_fd), level + 1); } -RefPtr WriteAppendRedirection::run(TheExecutionInputType input_value) +RefPtr WriteAppendRedirection::run(RefPtr shell) { Command command; - auto path_segments = m_path->run(input_value)->resolve_as_list(input_value); + auto path_segments = m_path->run(shell)->resolve_as_list(shell); StringBuilder builder; builder.join(" ", path_segments); @@ -1527,10 +1606,10 @@ void WriteRedirection::dump(int level) const print_indented(String::format("From %d", m_fd), level + 1); } -RefPtr WriteRedirection::run(TheExecutionInputType input_value) +RefPtr WriteRedirection::run(RefPtr shell) { Command command; - auto path_segments = m_path->run(input_value)->resolve_as_list(input_value); + auto path_segments = m_path->run(shell)->resolve_as_list(shell); StringBuilder builder; builder.join(" ", path_segments); @@ -1557,21 +1636,20 @@ void VariableDeclarations::dump(int level) const } } -RefPtr VariableDeclarations::run(TheExecutionInputType input_value) +RefPtr VariableDeclarations::run(RefPtr shell) { - auto shell = input_value; for (auto& var : m_variables) { - auto name_value = var.name->run(input_value)->resolve_as_list(input_value); + auto name_value = var.name->run(shell)->resolve_as_list(shell); ASSERT(name_value.size() == 1); auto name = name_value[0]; - auto value = var.value->run(input_value); + auto value = var.value->run(shell); if (value->is_list()) { - auto parts = value->resolve_as_list(input_value); + auto parts = value->resolve_as_list(shell); shell->set_local_variable(name, adopt(*new ListValue(move(parts)))); } else if (value->is_command()) { shell->set_local_variable(name, value); } else { - auto part = value->resolve_as_list(input_value); + auto part = value->resolve_as_list(shell); shell->set_local_variable(name, adopt(*new StringValue(part[0]))); } } @@ -1608,6 +1686,12 @@ VariableDeclarations::VariableDeclarations(Position position, Vector v : Node(move(position)) , m_variables(move(variables)) { + for (auto& decl : m_variables) { + if (decl.name->is_syntax_error() || decl.value->is_syntax_error()) { + set_is_syntax_error(); + break; + } + } } VariableDeclarations::~VariableDeclarations() @@ -1617,10 +1701,10 @@ VariableDeclarations::~VariableDeclarations() Value::~Value() { } -Vector Value::resolve_as_commands(TheExecutionInputType input_value) +Vector Value::resolve_as_commands(RefPtr shell) { Command command; - command.argv = resolve_as_list(input_value); + command.argv = resolve_as_list(shell); return { command }; } @@ -1635,11 +1719,11 @@ ListValue::~ListValue() { } -Vector ListValue::resolve_as_list(TheExecutionInputType input_value) +Vector ListValue::resolve_as_list(RefPtr shell) { Vector values; for (auto& value : m_contained_values) - values.append(value->resolve_as_list(input_value)); + values.append(value->resolve_as_list(shell)); return values; } @@ -1652,24 +1736,24 @@ CommandSequenceValue::~CommandSequenceValue() { } -Vector CommandSequenceValue::resolve_as_list(TheExecutionInputType) +Vector CommandSequenceValue::resolve_as_list(RefPtr) { // TODO: Somehow raise an "error". return {}; } -Vector CommandSequenceValue::resolve_as_commands(TheExecutionInputType) +Vector CommandSequenceValue::resolve_as_commands(RefPtr) { return m_contained_values; } -Vector CommandValue::resolve_as_list(TheExecutionInputType) +Vector CommandValue::resolve_as_list(RefPtr) { // TODO: Somehow raise an "error". return {}; } -Vector CommandValue::resolve_as_commands(TheExecutionInputType) +Vector CommandValue::resolve_as_commands(RefPtr) { return { m_command }; } @@ -1681,7 +1765,7 @@ JobValue::~JobValue() StringValue::~StringValue() { } -Vector StringValue::resolve_as_list(TheExecutionInputType) +Vector StringValue::resolve_as_list(RefPtr) { if (is_list()) { auto parts = StringView(m_string).split_view(m_split); @@ -1698,19 +1782,18 @@ Vector StringValue::resolve_as_list(TheExecutionInputType) GlobValue::~GlobValue() { } -Vector GlobValue::resolve_as_list(TheExecutionInputType input_value) +Vector GlobValue::resolve_as_list(RefPtr shell) { - auto shell = input_value; return shell->expand_globs(m_glob, shell->cwd); } SimpleVariableValue::~SimpleVariableValue() { } -Vector SimpleVariableValue::resolve_as_list(TheExecutionInputType input_value) +Vector SimpleVariableValue::resolve_as_list(RefPtr shell) { - if (auto value = resolve_without_cast(input_value); value != this) - return value->resolve_as_list(input_value); + if (auto value = resolve_without_cast(shell); value != this) + return value->resolve_as_list(shell); char* env_value = getenv(m_name.characters()); if (env_value == nullptr) @@ -1724,9 +1807,8 @@ Vector SimpleVariableValue::resolve_as_list(TheExecutionInputType input_ return res; } -RefPtr SimpleVariableValue::resolve_without_cast(TheExecutionInputType input_value) +RefPtr SimpleVariableValue::resolve_without_cast(RefPtr shell) { - auto shell = input_value; if (auto value = shell->lookup_local_variable(m_name)) return value; @@ -1737,9 +1819,8 @@ RefPtr SimpleVariableValue::resolve_without_cast(TheExecutionInputType in SpecialVariableValue::~SpecialVariableValue() { } -Vector SpecialVariableValue::resolve_as_list(TheExecutionInputType input_value) +Vector SpecialVariableValue::resolve_as_list(RefPtr shell) { - auto shell = input_value; switch (m_name) { case '?': return { String::number(shell->last_return_code) }; @@ -1753,9 +1834,8 @@ Vector SpecialVariableValue::resolve_as_list(TheExecutionInputType input TildeValue::~TildeValue() { } -Vector TildeValue::resolve_as_list(TheExecutionInputType input_value) +Vector TildeValue::resolve_as_list(RefPtr shell) { - auto shell = input_value; StringBuilder builder; builder.append("~"); builder.append(m_username); diff --git a/Shell/AST.h b/Shell/AST.h index 9094437badf..581a85ed89a 100644 --- a/Shell/AST.h +++ b/Shell/AST.h @@ -36,8 +36,6 @@ #include #include -using TheExecutionInputType = RefPtr; - namespace AST { struct HighlightMetadata { @@ -156,9 +154,9 @@ struct HitTestResult { class Value : public RefCounted { public: - virtual Vector resolve_as_list(TheExecutionInputType) = 0; - virtual Vector resolve_as_commands(TheExecutionInputType); - virtual RefPtr resolve_without_cast(TheExecutionInputType) { return this; } + virtual Vector resolve_as_list(RefPtr) = 0; + virtual Vector resolve_as_commands(RefPtr); + virtual RefPtr resolve_without_cast(RefPtr) { return this; } virtual ~Value(); virtual bool is_command() const { return false; } virtual bool is_glob() const { return false; } @@ -169,8 +167,8 @@ public: class CommandValue final : public Value { public: - virtual Vector resolve_as_list(TheExecutionInputType) override; - virtual Vector resolve_as_commands(TheExecutionInputType) override; + virtual Vector resolve_as_list(RefPtr) override; + virtual Vector resolve_as_commands(RefPtr) override; virtual ~CommandValue(); virtual bool is_command() const override { return true; } CommandValue(Command command) @@ -189,8 +187,8 @@ private: class CommandSequenceValue final : public Value { public: - virtual Vector resolve_as_list(TheExecutionInputType) override; - virtual Vector resolve_as_commands(TheExecutionInputType) override; + virtual Vector resolve_as_list(RefPtr) override; + virtual Vector resolve_as_commands(RefPtr) override; virtual ~CommandSequenceValue(); virtual bool is_command() const override { return true; } CommandSequenceValue(Vector commands) @@ -204,8 +202,8 @@ private: class JobValue final : public Value { public: - virtual Vector resolve_as_list(TheExecutionInputType) override { ASSERT_NOT_REACHED(); } - virtual Vector resolve_as_commands(TheExecutionInputType) override { ASSERT_NOT_REACHED(); } + virtual Vector resolve_as_list(RefPtr) override { ASSERT_NOT_REACHED(); } + virtual Vector resolve_as_commands(RefPtr) override { ASSERT_NOT_REACHED(); } virtual ~JobValue(); virtual bool is_job() const override { return true; } JobValue(RefPtr job) @@ -221,7 +219,7 @@ private: class ListValue final : public Value { public: - virtual Vector resolve_as_list(TheExecutionInputType) override; + virtual Vector resolve_as_list(RefPtr) override; virtual ~ListValue(); virtual bool is_list() const override { return true; } ListValue(Vector values); @@ -236,7 +234,7 @@ private: class StringValue final : public Value { public: - virtual Vector resolve_as_list(TheExecutionInputType) override; + virtual Vector resolve_as_list(RefPtr) override; virtual ~StringValue(); virtual bool is_string() const override { return m_split.is_null(); } virtual bool is_list() const override { return !m_split.is_null(); } @@ -253,7 +251,7 @@ private: class GlobValue final : public Value { public: - virtual Vector resolve_as_list(TheExecutionInputType) override; + virtual Vector resolve_as_list(RefPtr) override; virtual ~GlobValue(); virtual bool is_glob() const override { return true; } GlobValue(String glob) @@ -267,8 +265,8 @@ private: class SimpleVariableValue final : public Value { public: - virtual Vector resolve_as_list(TheExecutionInputType) override; - RefPtr resolve_without_cast(TheExecutionInputType) override; + virtual Vector resolve_as_list(RefPtr) override; + RefPtr resolve_without_cast(RefPtr) override; virtual ~SimpleVariableValue(); SimpleVariableValue(String name) : m_name(name) @@ -281,7 +279,7 @@ private: class SpecialVariableValue final : public Value { public: - virtual Vector resolve_as_list(TheExecutionInputType) override; + virtual Vector resolve_as_list(RefPtr) override; virtual ~SpecialVariableValue(); SpecialVariableValue(char name) : m_name(name) @@ -294,7 +292,7 @@ private: class TildeValue final : public Value { public: - virtual Vector resolve_as_list(TheExecutionInputType) override; + virtual Vector resolve_as_list(RefPtr) override; virtual ~TildeValue(); virtual bool is_string() const override { return true; } TildeValue(String name) @@ -308,8 +306,8 @@ private: class Node : public RefCounted { public: - virtual void dump(int level) = const 0; - virtual RefPtr run(TheExecutionInputType) = 0; + virtual void dump(int level) const = 0; + virtual RefPtr run(RefPtr) = 0; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) = 0; virtual Vector complete_for_editor(Shell&, size_t, RefPtr matching_node); Vector complete_for_editor(Shell& shell, size_t offset); @@ -329,14 +327,16 @@ public: virtual bool is_glob() const { return false; } virtual bool is_tilde() const { return false; } virtual bool is_variable_decls() const { return false; } - virtual bool is_syntax_error() const { return false; } + virtual bool is_syntax_error() const { return m_is_syntax_error; } virtual bool is_list() const { return false; } const Position& position() const { return m_position; } + void set_is_syntax_error() { m_is_syntax_error = true; } protected: Position m_position; + bool m_is_syntax_error { false }; }; class PathRedirectionNode : public Node { @@ -361,7 +361,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "And"; } @@ -377,6 +377,7 @@ public: private: virtual void dump(int level) const override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "ListConcatenate"; } @@ -393,7 +394,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "Background"; } @@ -409,7 +410,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual String class_name() const override { return "BarewordLiteral"; } virtual bool is_bareword() const override { return true; } @@ -424,7 +425,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual Vector complete_for_editor(Shell&, size_t, RefPtr matching_node) override; @@ -442,7 +443,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "CastToList"; } @@ -458,7 +459,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual String class_name() const override { return "CloseFdRedirection"; } virtual bool is_command() const override { return true; } @@ -473,7 +474,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override { ASSERT_NOT_REACHED(); } virtual String class_name() const override { return "CommandLiteral"; } virtual bool is_command() const override { return true; } @@ -490,7 +491,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual String class_name() const override { return "Comment"; } @@ -504,7 +505,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "DynamicEvaluate"; } @@ -528,7 +529,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "DoubleQuotedString"; } @@ -543,7 +544,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual String class_name() const override { return "Fd2FdRedirection"; } virtual bool is_command() const override { return true; } @@ -560,7 +561,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual String class_name() const override { return "Glob"; } virtual bool is_glob() const override { return true; } @@ -578,7 +579,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual Vector complete_for_editor(Shell&, size_t, RefPtr matching_node) override; @@ -596,7 +597,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "Join"; } @@ -614,7 +615,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "Or"; } @@ -631,7 +632,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "Pipe"; } @@ -648,7 +649,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual String class_name() const override { return "ReadRedirection"; } }; @@ -659,7 +660,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual String class_name() const override { return "ReadWriteRedirection"; } }; @@ -670,7 +671,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "Sequence"; } @@ -687,7 +688,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector complete_for_editor(Shell&, size_t, RefPtr matching_node) override; virtual HitTestResult hit_test_position(size_t) override; @@ -703,7 +704,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector complete_for_editor(Shell&, size_t, RefPtr matching_node) override; virtual HitTestResult hit_test_position(size_t) override; @@ -719,9 +720,10 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; + virtual Vector complete_for_editor(Shell&, size_t, RefPtr matching_node) override; virtual String class_name() const override { return "Juxtaposition"; } RefPtr m_left; @@ -736,7 +738,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual String class_name() const override { return "StringLiteral"; } @@ -750,7 +752,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "StringPartCompose"; } @@ -766,7 +768,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override { return { nullptr, nullptr }; } virtual String class_name() const override { return "SyntaxError"; } @@ -781,7 +783,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector complete_for_editor(Shell&, size_t, RefPtr matching_node) override; virtual HitTestResult hit_test_position(size_t) override; @@ -804,7 +806,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual String class_name() const override { return "VariableDeclarations"; } @@ -820,7 +822,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual String class_name() const override { return "WriteAppendRedirection"; } }; @@ -831,7 +833,7 @@ public: private: virtual void dump(int level) const override; - virtual RefPtr run(TheExecutionInputType) override; + virtual RefPtr run(RefPtr) override; virtual String class_name() const override { return "WriteRedirection"; } }; diff --git a/Shell/Parser.cpp b/Shell/Parser.cpp index c06c51577cc..ef7d23c79b4 100644 --- a/Shell/Parser.cpp +++ b/Shell/Parser.cpp @@ -112,7 +112,16 @@ RefPtr Parser::parse() { m_offset = 0; - return parse_toplevel(); + auto toplevel = parse_toplevel(); + + if (m_offset < m_input.length()) { + // Parsing stopped midway, this is a syntax error. + auto error_start = push_start(); + m_offset = m_input.length(); + return create(move(toplevel), create()); + } + + return toplevel; } RefPtr Parser::parse_toplevel() @@ -207,9 +216,10 @@ RefPtr Parser::parse_variable_decls() if (peek() == '(') { consume(); auto command = parse_pipe_sequence(); - expect(')'); if (!command) m_offset = start->offset; + else if (!expect(')')) + command->set_is_syntax_error(); expression = command; } } @@ -504,18 +514,26 @@ RefPtr Parser::parse_string() if (peek() == '"') { consume(); auto inner = parse_doublequoted_string_inner(); - if (!expect('"') || !inner) - return create(); + if (!inner) + inner = create(); + if (!expect('"')) { + inner = create(move(inner)); + inner->set_is_syntax_error(); + return inner; + } return create(move(inner)); // Double Quoted String } if (peek() == '\'') { consume(); auto text = consume_while(is_not('\'')); + bool is_error = false; if (!expect('\'')) - return create(); - - return create(move(text)); // String Literal + is_error = true; + auto result = create(move(text)); // String Literal + if (is_error) + result->set_is_syntax_error(); + return move(result); } return nullptr; @@ -648,11 +666,11 @@ RefPtr Parser::parse_evaluate() if (peek() == '(') { consume(); auto inner = parse_pipe_sequence(); - if (!inner || !expect(')')) + if (!inner) inner = create(); - else - inner = create(move(inner), true); - return inner; + if (!expect(')')) + inner->set_is_syntax_error(); + return create(move(inner), true); } auto inner = parse_expression(); @@ -718,9 +736,12 @@ RefPtr Parser::parse_bareword() if (builder.is_empty()) return nullptr; + auto current_end = m_offset; auto string = builder.to_string(); if (string.starts_with('~')) { String username; + RefPtr tilde, text; + auto first_slash_index = string.index_of("/"); if (first_slash_index.has_value()) { username = string.substring_view(1, first_slash_index.value() - 1); @@ -729,21 +750,33 @@ RefPtr Parser::parse_bareword() username = string.substring_view(1, string.length() - 1); string = ""; } - auto current_end = m_offset; - m_offset -= string.length(); - auto tilde = create(move(username)); - auto text_start = push_start(); - m_offset = current_end; + + // Synthesize a Tilde Node with the correct positioning information. + { + m_offset -= string.length(); + tilde = create(move(username)); + } + if (string.is_empty()) return tilde; - return create(move(tilde), create(move(string))); // Compose Varible Bareword + + // Synthesize a BarewordLiteral Node with the correct positioning information. + { + m_offset = tilde->position().end_offset; + auto text_start = push_start(); + m_offset = current_end; + text = create(move(string)); + } + + return create(move(tilde), move(text)); // Juxtaposition Varible Bareword } if (string.starts_with("\\~")) { // Un-escape the tilde, but only at the start (where it would be an expansion) + string = string.substring(1, string.length() - 1); } - return create(builder.to_string()); // Bareword Literal + return create(move(string)); // Bareword Literal } RefPtr Parser::parse_glob() @@ -757,17 +790,17 @@ RefPtr Parser::parse_glob() char ch = peek(); if (ch == '*' || ch == '?') { consume(); - // FIXME: Join all parts before making AST nodes StringBuilder textbuilder; if (bareword_part) { - ASSERT(bareword_part->is_bareword() || bareword_part->is_tilde()); StringView text; - if (bareword_part->is_tilde()) { + if (bareword_part->is_bareword()) { auto bareword = static_cast(bareword_part.ptr()); text = bareword->text(); } else { - auto tilde = static_cast(bareword_part.ptr()); - text = tilde->text(); + // FIXME: Allow composition of tilde+bareword with globs: '~/foo/bar/baz*' + putback(); + bareword_part->set_is_syntax_error(); + return bareword_part; } textbuilder.append(text); } diff --git a/Shell/Shell.cpp b/Shell/Shell.cpp index e18f1995bb0..398d8fe6313 100644 --- a/Shell/Shell.cpp +++ b/Shell/Shell.cpp @@ -324,6 +324,12 @@ int Shell::run_command(const StringView& cmd) if (!command) return 0; + if (command->is_syntax_error()) { + // FIXME: Provide descriptive messages for syntax errors. + fprintf(stderr, "Shell: Syntax error in command\n"); + return 1; + } + #ifdef SH_DEBUG dbg() << "Command follows"; command->dump(0); @@ -664,9 +670,9 @@ Vector Shell::complete(const Line::Editor& editor) return ast->complete_for_editor(*this, line.length()); } -Vector Shell::complete_path(const String& part, size_t offset) +Vector Shell::complete_path(const String& base, const String& part, size_t offset) { - auto token = part.substring_view(0, offset); + auto token = offset ? part.substring_view(0, offset) : ""; StringView original_token = token; String path; @@ -674,19 +680,30 @@ Vector Shell::complete_path(const String& part, size while (last_slash >= 0 && token[last_slash] != '/') --last_slash; - if (last_slash >= 0) { - // Split on the last slash. We'll use the first part as the directory - // to search and the second part as the token to complete. - path = token.substring_view(0, last_slash + 1); - if (path[0] != '/') - path = String::format("%s/%s", cwd.characters(), path.characters()); - path = LexicalPath::canonicalized_path(path); - token = token.substring_view(last_slash + 1, token.length() - last_slash - 1); + StringBuilder path_builder; + auto init_slash_part = token.substring_view(0, last_slash + 1); + auto last_slash_part = token.substring_view(last_slash + 1, token.length() - last_slash - 1); + + // Depending on the base, we will have to prepend cwd. + if (base.is_empty()) { + // '' /foo -> absolute + // '' foo -> relative + if (!token.starts_with('/')) + path_builder.append(cwd); + path_builder.append('/'); + path_builder.append(init_slash_part); } else { - // We have no slashes, so the directory to search is the current - // directory and the token to complete is just the original token. - path = cwd; + // /foo * -> absolute + // foo * -> relative + if (!base.starts_with('/')) + path_builder.append(cwd); + path_builder.append('/'); + path_builder.append(base); + path_builder.append('/'); + path_builder.append(init_slash_part); } + path = path_builder.build(); + token = last_slash_part; // the invariant part of the token is actually just the last segment // e. in `cd /foo/bar', 'bar' is the invariant @@ -727,7 +744,7 @@ Vector Shell::complete_program_name(const String& na }); if (!match) - return complete_path(name, offset); + return complete_path("", name, offset); String completion = *match; editor->suggest(escape_token(name).length(), 0); @@ -753,7 +770,7 @@ Vector Shell::complete_program_name(const String& na Vector Shell::complete_variable(const String& name, size_t offset) { Vector suggestions; - auto pattern = name.substring_view(0, offset); + auto pattern = offset ? name.substring_view(0, offset) : ""; editor->suggest(offset); @@ -780,6 +797,27 @@ Vector Shell::complete_variable(const String& name, return suggestions; } +Vector Shell::complete_user(const String& name, size_t offset) +{ + Vector suggestions; + auto pattern = offset ? name.substring_view(0, offset) : ""; + + editor->suggest(offset); + + Core::DirIterator di("/home", Core::DirIterator::SkipParentAndBaseDir); + + if (di.has_error()) + return suggestions; + + while (di.has_next()) { + String name = di.next_path(); + if (name.starts_with(pattern)) + suggestions.append(name); + } + + return suggestions; +} + bool Shell::read_single_line() { take_back_stdin(); diff --git a/Shell/Shell.h b/Shell/Shell.h index 6be974027bf..18b1c275a78 100644 --- a/Shell/Shell.h +++ b/Shell/Shell.h @@ -92,9 +92,10 @@ public: void highlight(Line::Editor&) const; Vector complete(const Line::Editor&); - Vector complete_path(const String&, size_t offset); + Vector complete_path(const String& base, const String&, size_t offset); Vector complete_program_name(const String&, size_t offset); Vector complete_variable(const String&, size_t offset); + Vector complete_user(const String&, size_t offset); void take_back_stdin();