From 2714bba3f00d92c4e7ba400c35bd54df19817571 Mon Sep 17 00:00:00 2001 From: AnotherTest Date: Mon, 8 Jun 2020 11:38:37 +0430 Subject: [PATCH] Shell: Highlight redirections --- Shell/Parser.cpp | 32 ++++++++++++++++++++++++++------ Shell/Parser.h | 1 + Shell/Shell.cpp | 19 +++++++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Shell/Parser.cpp b/Shell/Parser.cpp index fad6d5e9a58..41bccbfe91e 100644 --- a/Shell/Parser.cpp +++ b/Shell/Parser.cpp @@ -147,6 +147,8 @@ Vector Parser::parse() if (ch == '>') { commit_token(Token::Special); begin_redirect_write(STDOUT_FILENO); + ASSERT(!m_redirections.is_empty()); + m_redirections.last().redirection_op_start = m_position; // Search for another > for append. push_state(State::InWriteAppendOrRedirectionPath); @@ -155,6 +157,8 @@ Vector Parser::parse() if (ch == '<') { commit_token(Token::Special); begin_redirect_read(STDIN_FILENO); + ASSERT(!m_redirections.is_empty()); + m_redirections.last().redirection_op_start = m_position; push_state(State::InRedirectionPath); break; } @@ -208,11 +212,15 @@ Vector Parser::parse() if (m_input.characters()[redir_end] == '>') { begin_redirect_write(fd); + ASSERT(!m_redirections.is_empty()); + m_redirections.last().redirection_op_start = m_position; // Search for another > for append. push_state(State::InWriteAppendOrRedirectionPath); } if (m_input.characters()[redir_end] == '<') { begin_redirect_read(fd); + ASSERT(!m_redirections.is_empty()); + m_redirections.last().redirection_op_start = m_position; push_state(State::InRedirectionPath); } @@ -227,6 +235,8 @@ Vector Parser::parse() if (next_ch == '>') { commit_token(Token::Special); begin_redirect_write(ch - '0'); + ASSERT(!m_redirections.is_empty()); + m_redirections.last().redirection_op_start = m_position; ++i; // Search for another > for append. @@ -236,6 +246,8 @@ Vector Parser::parse() if (next_ch == '<') { commit_token(Token::Special); begin_redirect_read(ch - '0'); + ASSERT(!m_redirections.is_empty()); + m_redirections.last().redirection_op_start = m_position; ++i; push_state(State::InRedirectionPath); @@ -251,7 +263,7 @@ Vector Parser::parse() pop_state(); push_state(State::InRedirectionPath); ASSERT(m_redirections.size()); - m_redirections[m_redirections.size() - 1].type = Redirection::FileWriteAppend; + m_redirections.last().type = Redirection::FileWriteAppend; break; } @@ -263,15 +275,11 @@ Vector Parser::parse() if (ch == '<') { commit_token(Token::Special); begin_redirect_read(STDIN_FILENO); - pop_state(); - push_state(State::InRedirectionPath); break; } if (ch == '>') { commit_token(Token::Special); begin_redirect_read(STDOUT_FILENO); - pop_state(); - push_state(State::InRedirectionPath); break; } if (ch == '|') { @@ -292,8 +300,20 @@ Vector Parser::parse() push_state(State::InSingleQuotes); break; } - if (ch == ' ') + if (ch == ' ') { + if (m_token.is_empty()) { + // foo > bar + // ^ We are at this space, we want to ignore it but not leave the state. + break; + } + commit_token(Token::Special); + if (m_tokens.is_empty()) { + fprintf(stderr, "Syntax error: Redirection without a path\n"); + return {}; + } + pop_state(); break; + } m_token.append(ch); break; case State::InSingleQuotes: diff --git a/Shell/Parser.h b/Shell/Parser.h index e9fcb5f9f49..112747ce3ea 100644 --- a/Shell/Parser.h +++ b/Shell/Parser.h @@ -61,6 +61,7 @@ struct Redirection { Type type; int fd { -1 }; int rewire_fd { -1 }; + size_t redirection_op_start { 0 }; Token path {}; }; diff --git a/Shell/Shell.cpp b/Shell/Shell.cpp index f1bcf656202..876bf7ea649 100644 --- a/Shell/Shell.cpp +++ b/Shell/Shell.cpp @@ -1482,17 +1482,36 @@ void Shell::cache_path() void Shell::highlight(Line::Editor& editor) const { StringBuilder builder; + bool is_offset_by_string_start = false; if (m_should_continue == ExitCodeOrContinuationRequest::DoubleQuotedString) { builder.append('"'); + is_offset_by_string_start = true; } if (m_should_continue == ExitCodeOrContinuationRequest::SingleQuotedString) { builder.append('\''); + is_offset_by_string_start = true; } builder.append(editor.line()); auto commands = Parser { builder.string_view() }.parse(); auto first_command { true }; for (auto& command : commands) { for (auto& subcommand : command.subcommands) { + auto& redirections = subcommand.redirections; + for (auto& redirection : redirections) { + if (redirection.type == Redirection::Pipe) + continue; + if (redirection.path.length == 0) + continue; + Line::Style redirection_style { Line::Style::Foreground(0x87, 0x9b, 0xcd) }; // 25% darkened periwinkle :) + auto end = redirection.path.end; + auto redirection_op_start = redirection.redirection_op_start; + if (is_offset_by_string_start) { + end--; + redirection_op_start--; + } + + editor.stylize({ redirection_op_start, end }, redirection_style); + } auto first { true }; for (auto& arg : subcommand.args) { auto start = arg.end - arg.length;