Skip to content

Commit eccbab6

Browse files
authored
chore: refactor terminal (#829)
* Refactor terminal * Rename Terminal class for backwards compatibility * Fix microtest * Fix microtest * Remove default max command length * Remove accidental tracer
1 parent 2717911 commit eccbab6

File tree

2 files changed

+312
-283
lines changed

2 files changed

+312
-283
lines changed

services/util/Terminal.cpp

-275
Original file line numberDiff line numberDiff line change
@@ -3,266 +3,6 @@
33

44
namespace services
55
{
6-
Terminal::Terminal(infra::MemoryRange<uint8_t> bufferQueue, infra::BoundedDeque<infra::BoundedString::WithStorage<MaxBuffer>>& history, hal::SerialCommunication& communication, services::Tracer& tracer)
7-
: queue(bufferQueue, [this]
8-
{
9-
HandleInput();
10-
})
11-
, history(history)
12-
, tracer(tracer)
13-
{
14-
communication.ReceiveData([this](infra::ConstByteRange data)
15-
{
16-
queue.AddFromInterrupt(data);
17-
});
18-
Print(state.prompt);
19-
}
20-
21-
void Terminal::Print(const char* message)
22-
{
23-
tracer.Continue() << message;
24-
}
25-
26-
void Terminal::HandleInput()
27-
{
28-
while (!queue.Empty())
29-
HandleChar(static_cast<char>(queue.Get()));
30-
}
31-
32-
void Terminal::HandleChar(char c)
33-
{
34-
if (state.processingEscapeSequence)
35-
state.processingEscapeSequence = ProcessEscapeSequence(c);
36-
else
37-
HandleNonEscapeChar(c);
38-
}
39-
40-
void Terminal::HandleNonEscapeChar(char c)
41-
{
42-
switch (c)
43-
{
44-
case '\n':
45-
break;
46-
case '\r':
47-
ProcessEnter();
48-
break;
49-
case 27:
50-
state.processingEscapeSequence = true;
51-
break;
52-
case '\b':
53-
case '\x7F':
54-
ProcessBackspace();
55-
break;
56-
case 1: // ctrl-a
57-
MoveCursorHome();
58-
break;
59-
case 2: // ctrl-b
60-
MoveCursorLeft();
61-
break;
62-
case 3: // ctrl-c
63-
OverwriteBuffer("");
64-
break;
65-
case 4: // ctrl-d
66-
ProcessDelete();
67-
break;
68-
case 5: // ctrl-e
69-
MoveCursorEnd();
70-
break;
71-
case 6: // ctrl-f
72-
MoveCursorRight();
73-
break;
74-
case 14: // ctrl-n
75-
HistoryForward();
76-
break;
77-
case 16: // ctrl-p
78-
HistoryBackward();
79-
break;
80-
default:
81-
SendNonEscapeChar(c);
82-
break;
83-
}
84-
}
85-
86-
bool Terminal::ProcessEscapeSequence(char in)
87-
{
88-
static const infra::BoundedConstString ignoredEscapeCharacters = ";[O0123456789";
89-
if (ignoredEscapeCharacters.find(in) != infra::BoundedConstString::npos)
90-
return true;
91-
92-
switch (in)
93-
{
94-
case 'A':
95-
HistoryBackward();
96-
break;
97-
case 'B':
98-
HistoryForward();
99-
break;
100-
case 'C':
101-
MoveCursorRight();
102-
break;
103-
case 'D':
104-
MoveCursorLeft();
105-
break;
106-
case 'F':
107-
MoveCursorEnd();
108-
break;
109-
case 'H':
110-
MoveCursorHome();
111-
break;
112-
default:
113-
SendBell();
114-
break;
115-
}
116-
117-
return false;
118-
}
119-
120-
void Terminal::ProcessEnter()
121-
{
122-
Print("\r\n");
123-
124-
if (buffer.size() > 0)
125-
{
126-
StoreHistory(buffer);
127-
OnData(buffer);
128-
}
129-
130-
buffer.clear();
131-
state.cursorPosition = 0;
132-
Print(state.prompt);
133-
}
134-
135-
void Terminal::ProcessBackspace()
136-
{
137-
MoveCursorLeft();
138-
ProcessDelete();
139-
}
140-
141-
void Terminal::ProcessDelete()
142-
{
143-
if (state.cursorPosition < buffer.size())
144-
EraseCharacterUnderCursor();
145-
else
146-
SendBell();
147-
}
148-
149-
void Terminal::EraseCharacterUnderCursor()
150-
{
151-
assert(state.cursorPosition < buffer.size());
152-
153-
if (buffer.size() == state.cursorPosition + 1)
154-
buffer.pop_back();
155-
else
156-
{
157-
std::rotate(std::next(buffer.begin(), state.cursorPosition), std::next(buffer.begin(), state.cursorPosition + 1), buffer.end());
158-
buffer = buffer.substr(0, buffer.size() - 1);
159-
tracer.Continue() << ByteRangeAsString(infra::MakeRange(reinterpret_cast<const uint8_t*>(std::next(buffer.begin(), state.cursorPosition)), reinterpret_cast<const uint8_t*>(buffer.end())));
160-
}
161-
162-
Print(" \b");
163-
164-
for (uint32_t i = buffer.size(); i > state.cursorPosition; --i)
165-
tracer.Continue() << '\b';
166-
}
167-
168-
void Terminal::MoveCursorHome()
169-
{
170-
state.cursorPosition = 0;
171-
tracer.Continue() << '\r';
172-
Print(state.prompt);
173-
}
174-
175-
void Terminal::MoveCursorEnd()
176-
{
177-
if (buffer.size() > 0 && state.cursorPosition < buffer.size())
178-
tracer.Continue() << ByteRangeAsString(infra::MakeRange(reinterpret_cast<const uint8_t*>(std::next(buffer.begin(), state.cursorPosition)), reinterpret_cast<const uint8_t*>(buffer.end())));
179-
state.cursorPosition = buffer.size();
180-
}
181-
182-
void Terminal::MoveCursorLeft()
183-
{
184-
if (state.cursorPosition > 0)
185-
{
186-
tracer.Continue() << '\b';
187-
--state.cursorPosition;
188-
}
189-
else
190-
SendBell();
191-
}
192-
193-
void Terminal::MoveCursorRight()
194-
{
195-
if (state.cursorPosition < buffer.size())
196-
{
197-
tracer.Continue() << buffer[state.cursorPosition];
198-
++state.cursorPosition;
199-
}
200-
else
201-
SendBell();
202-
}
203-
204-
void Terminal::StoreHistory(infra::BoundedString element)
205-
{
206-
if (history.full())
207-
history.pop_front();
208-
209-
history.push_back(buffer);
210-
state.historyIndex = history.size();
211-
}
212-
213-
void Terminal::OverwriteBuffer(infra::BoundedConstString element)
214-
{
215-
std::size_t previousSize = buffer.size();
216-
buffer.assign(element);
217-
218-
tracer.Continue() << '\r';
219-
Print(state.prompt);
220-
221-
if (buffer.size() > 0)
222-
tracer.Continue() << buffer;
223-
224-
for (std::size_t size = buffer.size(); size < previousSize; ++size)
225-
tracer.Continue() << ' ';
226-
227-
for (std::size_t size = buffer.size(); size < previousSize; ++size)
228-
tracer.Continue() << '\b';
229-
230-
state.cursorPosition = buffer.size();
231-
}
232-
233-
void Terminal::HistoryForward()
234-
{
235-
if (!history.empty() && state.historyIndex < history.size() - 1)
236-
OverwriteBuffer(history[++state.historyIndex]);
237-
else
238-
OverwriteBuffer("");
239-
}
240-
241-
void Terminal::HistoryBackward()
242-
{
243-
if (state.historyIndex > 0)
244-
OverwriteBuffer(history[--state.historyIndex]);
245-
else
246-
SendBell();
247-
}
248-
249-
void Terminal::SendNonEscapeChar(char c)
250-
{
251-
if (c > 31 && c < 127)
252-
{
253-
tracer.Continue() << c;
254-
buffer.push_back(c);
255-
state.cursorPosition++;
256-
}
257-
else
258-
SendBell();
259-
}
260-
261-
void Terminal::SendBell()
262-
{
263-
tracer.Continue() << '\a';
264-
}
265-
2666
bool TerminalCommands::ProcessCommand(infra::BoundedConstString data)
2677
{
2688
infra::Tokenizer tokenizer(data, ' ');
@@ -283,19 +23,4 @@ namespace services
28323
else
28424
return false;
28525
}
286-
287-
TerminalWithCommandsImpl::TerminalWithCommandsImpl(infra::MemoryRange<uint8_t> bufferQueue, infra::BoundedDeque<infra::BoundedString::WithStorage<MaxBuffer>>& history, hal::SerialCommunication& communication, services::Tracer& tracer)
288-
: services::Terminal(bufferQueue, history, communication, tracer)
289-
{}
290-
291-
void TerminalWithCommandsImpl::OnData(infra::BoundedConstString data)
292-
{
293-
bool commandProcessed = NotifyObservers([data](TerminalCommands& observer)
294-
{
295-
return observer.ProcessCommand(data);
296-
});
297-
298-
if (!commandProcessed)
299-
Print("Unrecognized command.");
300-
}
30126
}

0 commit comments

Comments
 (0)