MalachScript/repl/InputWindow.hpp

262 lines
8.4 KiB
C++

#ifndef MALACHSCRIPT_INPUTWINDOW_HPP
#define MALACHSCRIPT_INPUTWINDOW_HPP
#include <ncurses.h>
#include <string>
#include <vector>
namespace MalachScriptRepl {
class InputWindow {
private:
WINDOW* _window;
std::vector<std::string> _lines = {""};
int _row = 0;
int _col = 0;
int _rightPos = 0;
int _scroll = 0;
int _lineCount;
std::function<void(const std::vector<std::string>&)> _onChange;
const MalachScript::Diagnostics::Diagnostic* _diag;
public:
InputWindow(int height, int width, int y, int x) {
_window = newwin(height, width, y, x);
keypad(_window, true);
_lineCount = height;
}
inline void RegisterOnChange(std::function<void(const std::vector<std::string>&)> listener) {
_onChange = listener;
}
inline void Refresh() { wrefresh(_window); }
inline void Clear() { wclear(_window); }
[[nodiscard]] inline int GetInput() const { return wgetch(_window); }
inline std::pair<int, int> GetCursorPosition() {
int row, col;
getyx(_window, row, col);
return std::pair<int, int>(row, col);
}
inline void MoveCursorPosition(int row, int col) {
auto& line = _lines[row + _scroll];
if (col > (int)line.size()) {
col = line.size();
}
wmove(_window, row, col);
}
inline void MoveCursorLeft() {
if (_col > 0) {
wmove(_window, _row, --_col);
_rightPos = _col;
}
}
inline void MoveCursorRight() {
auto& line = _lines[_row + _scroll];
if (_col < (int)line.size()) {
wmove(_window, _row, ++_col);
_rightPos = _col;
}
}
inline void MoveCursorUp() {
if (_row > 0) {
_col = _rightPos;
if (_col > (int)_lines[_row - 1 + _scroll].size()) {
_col = (int)_lines[_row - 1 + _scroll].size();
}
wmove(_window, --_row, _col);
} else {
ScrollUp();
}
}
inline void MoveCursorDown() {
if (_row < _lineCount - 1 && _row + _scroll < (int)_lines.size() - 1) {
_col = _rightPos;
if (_col > (int)_lines[_row + 1 + _scroll].size()) {
_col = (int)_lines[_row + 1 + _scroll].size();
}
wmove(_window, ++_row, _col);
} else {
ScrollDown();
}
}
inline void Backspace() {
if (_col > 0) {
mvwdelch(_window, _row, --_col);
auto& line = _lines[_row + _scroll];
if (_col < (int)line.size()) {
line = line.erase(_col, 1);
_onChange(_lines);
}
}
}
inline void Return() {
_lines.insert(_lines.begin() + _row + _scroll + 1, "");
if (_row >= _lineCount - 1) {
ScrollDown();
} else {
_row++;
_col = 0;
for (size_t i = _row - 1; i < _lines.size(); i++) {
wmove(_window, i, 0);
wclrtoeol(_window);
waddstr(_window, (char*)_lines[i].c_str());
}
wmove(_window, _row, _col);
}
_onChange(_lines);
}
inline void Tab() {
wmove(_window, _row, 0);
wclrtoeol(_window);
auto& line = _lines[_row + _scroll];
if ((int)line.length() <= _col) {
line += " ";
} else {
for (size_t i = 0; i < 4; i++) {
line.insert(line.begin() + _col, ' ');
}
}
waddstr(_window, (char*)line.c_str());
_col += 4;
_rightPos = _col;
wmove(_window, _row, _col);
_onChange(_lines);
}
inline void GoToEndOfLine() {
_col = _lines[_row + _scroll].size();
_rightPos = _col;
wmove(_window, _row, _col);
}
inline void GoToStartOfLine() {
_col = 0;
_rightPos = 0;
wmove(_window, _row, _col);
}
inline void Input(int c) {
wmove(_window, _row, 0);
auto& line = _lines[_row + _scroll];
if ((int)line.length() <= _col) {
line += (char)c;
} else {
line.insert(line.begin() + _col, c);
}
waddstr(_window, (char*)line.c_str());
_col++;
_rightPos = _col;
wmove(_window, _row, _col);
_onChange(_lines);
}
inline void ResetText() {
std::string script;
for (size_t i = 0; i < _lines.size(); i++) {
script += _lines[i];
if (i != _lines.size() - 1) {
script += '\n';
}
}
SetScriptWithDiagnostics(script, _diag);
}
inline void ScrollDown() {
if (_scroll + _row < (int)_lines.size() - 1) {
_scroll++;
ResetText();
_col = _rightPos;
if (_col > (int)_lines[_row + _scroll].size()) {
_col = (int)_lines[_row + _scroll].size();
}
wmove(_window, _row, _col);
}
}
inline void ScrollUp() {
if (_scroll <= 0) {
return;
}
_scroll--;
ResetText();
_col = _rightPos;
if (_col > (int)_lines[_row + _scroll].size()) {
_col = (int)_lines[_row + _scroll].size();
}
wmove(_window, _row, _col);
}
inline void SetScriptWithDiagnostics(std::string& script, const MalachScript::Diagnostics::Diagnostic* diag) {
_diag = diag;
auto yx = GetCursorPosition();
Clear();
script += " ";
size_t linenum = 0;
size_t index = 0;
size_t linestart = 0;
size_t lineend = 0;
std::istringstream f((char*)script.data());
std::string line;
while (std::getline(f, line)) {
if ((int)linenum == _scroll) {
linestart = index;
}
if ((int)linenum == _scroll + _lineCount - 1 || linenum == _lines.size() - 1) {
lineend = index + line.size() + 1;
break;
}
index += line.size() + 1;
linenum++;
}
if (lineend == 0) {
lineend = script.size();
}
if (diag != nullptr && diag->GetSpan().GetStart() >= linestart && diag->GetSpan().GetStart() <= lineend) {
auto start = diag->GetSpan().GetStart() - linestart;
auto end = diag->GetSpan().GetEnd() - linestart + 1;
if (diag->GetSpan().GetStart() > script.size()) {
start = script.size() - linestart - 1;
}
if (diag->GetSpan().GetEnd() >= script.size()) {
end = script.size() - linestart;
}
waddnstr(_window, (char*)script.substr(linestart, start).data(), start);
wattron(_window, COLOR_PAIR(1));
wattron(_window, A_UNDERLINE);
waddnstr(_window, (char*)script.substr(start + linestart, end - start).data(), end - start);
wattroff(_window, A_UNDERLINE);
wattroff(_window, COLOR_PAIR(1));
waddnstr(_window, (char*)script.substr(end + linestart, script.size() - end + linestart + 1).data(),
script.size() - end + linestart + 1);
if (start >= script.size() - 1) {
wattron(_window, COLOR_PAIR(1));
waddch(_window, ' ');
wattroff(_window, COLOR_PAIR(1));
}
} else {
waddstr(_window, (char*)script.substr(linestart).data());
}
MoveCursorPosition(yx.first, yx.second);
Refresh();
}
};
}
#endif // MALACHSCRIPT_INPUTWINDOW_HPP