#ifndef MALACHSCRIPT_INPUTWINDOW_HPP #define MALACHSCRIPT_INPUTWINDOW_HPP #include #include #include namespace MalachScriptRepl { class InputWindow { private: WINDOW* _window; std::vector _lines = {u8""}; int _row = 0; int _col = 0; int _rightPos = 0; int _scroll = 0; int _lineCount; std::function&)> _onChange; 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&)> listener) { _onChange = listener; } inline void Refresh() { wrefresh(_window); } inline void Clear() { wclear(_window); } [[nodiscard]] inline int GetInput() const { return wgetch(_window); } inline std::pair GetCursorPosition() { int row, col; getyx(_window, row, col); return std::pair(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, u8""); 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); } } inline void Tab() { wmove(_window, _row, 0); wclrtoeol(_window); auto& line = _lines[_row + _scroll]; if ((int)line.length() <= _col) { line += u8" "; } else { for (size_t i = 0; i < 4; i++) { line.insert(line.begin() + _col, u8' '); } } 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 += (char8_t)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() { auto pos = GetCursorPosition(); wclear(_window); for (size_t i = 0; i < _lines.size() && (int)i < _lineCount; i++) { wmove(_window, i, 0); waddnstr(_window, (char*)_lines[i + _scroll].c_str(), _lines[i + _scroll].size()); } MoveCursorPosition(pos.first, pos.second); Refresh(); } 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(const std::u8string& script, const MalachScript::Diagnostics::Diagnostic* diag) { auto yx = GetCursorPosition(); Clear(); size_t linenum = 0; size_t index = 0; size_t linestart = 0; std::istringstream f((char*)script.data()); std::string line; while (std::getline(f, line)) { if ((int)linenum == _scroll) { linestart = index; } index += line.size() + 1; linenum++; } if (diag != nullptr) { auto start = diag->GetSpan().GetStart() - linestart; auto end = diag->GetSpan().GetEnd() - linestart; if (end > script.size()) { end = script.size(); if (start == end && start > 0) { start--; } } waddnstr(_window, (char*)script.substr(linestart).data(), start); wattron(_window, COLOR_PAIR(1)); waddnstr(_window, (char*)script.substr(start + linestart).data(), end - start); wattroff(_window, COLOR_PAIR(1)); waddnstr(_window, (char*)script.substr(end + linestart).data(), script.size() - end); 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