#ifndef MALACHSCRIPT_INPUTWINDOW_HPP #define MALACHSCRIPT_INPUTWINDOW_HPP #include #include #include namespace MalachScriptRepl { class InputWindow { private: WINDOW* _window; std::vector _lines = {""}; int _row = 0; int _col = 0; int _rightPos = 0; int _scroll = 0; int _lineCount; std::function&)> _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&)> 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, ""); 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