summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2020-06-08 11:38:37 +0430
committerAndreas Kling <kling@serenityos.org>2020-06-08 09:27:51 +0200
commit2714bba3f00d92c4e7ba400c35bd54df19817571 (patch)
treeab41ea4df16bcaa91d320a8be75ba719eb403f9b
parent07c070745fb402eed0d7e3ec1567edfabb9b9946 (diff)
downloadserenity-2714bba3f00d92c4e7ba400c35bd54df19817571.zip
Shell: Highlight redirections
-rw-r--r--Shell/Parser.cpp32
-rw-r--r--Shell/Parser.h1
-rw-r--r--Shell/Shell.cpp19
3 files changed, 46 insertions, 6 deletions
diff --git a/Shell/Parser.cpp b/Shell/Parser.cpp
index fad6d5e9a5..41bccbfe91 100644
--- a/Shell/Parser.cpp
+++ b/Shell/Parser.cpp
@@ -147,6 +147,8 @@ Vector<Command> 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<Command> 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<Command> 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<Command> 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<Command> 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<Command> 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<Command> 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<Command> 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 e9fcb5f9f4..112747ce3e 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 f1bcf65620..876bf7ea64 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;