summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Shell/LineEditor.cpp71
-rw-r--r--Shell/LineEditor.h11
2 files changed, 78 insertions, 4 deletions
diff --git a/Shell/LineEditor.cpp b/Shell/LineEditor.cpp
index e32e4d7f5d..371afc3cb3 100644
--- a/Shell/LineEditor.cpp
+++ b/Shell/LineEditor.cpp
@@ -19,8 +19,24 @@ void LineEditor::add_to_history(const String& 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()
{
+ m_history_cursor = m_history.size();
for (;;) {
char keybuf[16];
ssize_t nread = read(0, keybuf, sizeof(keybuf));
@@ -37,17 +53,64 @@ String LineEditor::get_line()
m_buffer.clear();
putchar('\n');
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) {
char ch = keybuf[i];
if (ch == 0)
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 (m_buffer.is_empty())
continue;
diff --git a/Shell/LineEditor.h b/Shell/LineEditor.h
index e5668ff349..60d0e643b0 100644
--- a/Shell/LineEditor.h
+++ b/Shell/LineEditor.h
@@ -14,10 +14,21 @@ public:
const Vector<String>& history() const { return m_history; }
private:
+ void clear_line();
+ void append(const String&);
+
Vector<char, 1024> m_buffer;
int m_cursor { 0 };
// FIXME: This should be something more take_first()-friendly.
Vector<String> m_history;
+ int m_history_cursor { 0 };
int m_history_capacity { 100 };
+
+ enum class InputState {
+ Free,
+ ExpectBracket,
+ ExpectFinal,
+ };
+ InputState m_state { InputState::Free };
};