mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 15:07:45 +00:00
Shell: Allow browsing history with up/down arrow keys.
This commit is contained in:
parent
16a5a76445
commit
ad1c3c748f
2 changed files with 78 additions and 4 deletions
|
@ -19,8 +19,24 @@ void LineEditor::add_to_history(const String& line)
|
||||||
m_history.append(line);
|
m_history.append(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LineEditor::clear_line()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_buffer.size(); ++i)
|
||||||
|
fputc(0x8, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
m_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineEditor::append(const String& string)
|
||||||
|
{
|
||||||
|
m_buffer.append(string.characters(), string.length());
|
||||||
|
fputs(string.characters(), stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
String LineEditor::get_line()
|
String LineEditor::get_line()
|
||||||
{
|
{
|
||||||
|
m_history_cursor = m_history.size();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char keybuf[16];
|
char keybuf[16];
|
||||||
ssize_t nread = read(0, keybuf, sizeof(keybuf));
|
ssize_t nread = read(0, keybuf, sizeof(keybuf));
|
||||||
|
@ -37,17 +53,64 @@ String LineEditor::get_line()
|
||||||
m_buffer.clear();
|
m_buffer.clear();
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
return String::empty();
|
return String::empty();
|
||||||
} else {
|
|
||||||
perror("read failed");
|
|
||||||
// FIXME: exit()ing here is a bit off. Should communicate failure to caller somehow instead.
|
|
||||||
exit(2);
|
|
||||||
}
|
}
|
||||||
|
perror("read failed");
|
||||||
|
// FIXME: exit()ing here is a bit off. Should communicate failure to caller somehow instead.
|
||||||
|
exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ssize_t i = 0; i < nread; ++i) {
|
for (ssize_t i = 0; i < nread; ++i) {
|
||||||
char ch = keybuf[i];
|
char ch = keybuf[i];
|
||||||
if (ch == 0)
|
if (ch == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
switch (m_state) {
|
||||||
|
case InputState::ExpectBracket:
|
||||||
|
if (ch == '[') {
|
||||||
|
m_state = InputState::ExpectFinal;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
m_state = InputState::Free;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case InputState::ExpectFinal:
|
||||||
|
switch (ch) {
|
||||||
|
case 'A': // up
|
||||||
|
if (m_history_cursor > 0)
|
||||||
|
--m_history_cursor;
|
||||||
|
clear_line();
|
||||||
|
if (m_history_cursor < m_history.size())
|
||||||
|
append(m_history[m_history_cursor]);
|
||||||
|
m_state = InputState::Free;
|
||||||
|
continue;
|
||||||
|
case 'B': // down
|
||||||
|
if (m_history_cursor < m_history.size())
|
||||||
|
++m_history_cursor;
|
||||||
|
clear_line();
|
||||||
|
if (m_history_cursor < m_history.size())
|
||||||
|
append(m_history[m_history_cursor]);
|
||||||
|
m_state = InputState::Free;
|
||||||
|
continue;
|
||||||
|
case 'D': // left
|
||||||
|
m_state = InputState::Free;
|
||||||
|
continue;
|
||||||
|
case 'C': // right
|
||||||
|
m_state = InputState::Free;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
dbgprintf("Shell: Unhandled final: %b (%c)\n", ch, ch);
|
||||||
|
m_state = InputState::Free;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case InputState::Free:
|
||||||
|
if (ch == 27) {
|
||||||
|
m_state = InputState::ExpectBracket;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ch == 8 || ch == g.termios.c_cc[VERASE]) {
|
if (ch == 8 || ch == g.termios.c_cc[VERASE]) {
|
||||||
if (m_buffer.is_empty())
|
if (m_buffer.is_empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -14,10 +14,21 @@ public:
|
||||||
const Vector<String>& history() const { return m_history; }
|
const Vector<String>& history() const { return m_history; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void clear_line();
|
||||||
|
void append(const String&);
|
||||||
|
|
||||||
Vector<char, 1024> m_buffer;
|
Vector<char, 1024> m_buffer;
|
||||||
int m_cursor { 0 };
|
int m_cursor { 0 };
|
||||||
|
|
||||||
// FIXME: This should be something more take_first()-friendly.
|
// FIXME: This should be something more take_first()-friendly.
|
||||||
Vector<String> m_history;
|
Vector<String> m_history;
|
||||||
|
int m_history_cursor { 0 };
|
||||||
int m_history_capacity { 100 };
|
int m_history_capacity { 100 };
|
||||||
|
|
||||||
|
enum class InputState {
|
||||||
|
Free,
|
||||||
|
ExpectBracket,
|
||||||
|
ExpectFinal,
|
||||||
|
};
|
||||||
|
InputState m_state { InputState::Free };
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue