gigaTerminal text editor |
git clone git://git.dimitrijedobrota.com/giga.git |
Log | Files | Refs | README | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
main.cpp (7522B)
0 #include <filesystem>
1 #include <fstream>
2 #include <memory>
3 #include <span>
4 #include <string>
5 #include <vector>
7 #include <display/display.hpp>
8 #include <display/utility.hpp>
9 #include <display/window.hpp>
11 #include "layout_dynamic.hpp"
13 class File
14 {
15 public:
16 using container_t = std::vector<std::string>;
18 explicit File(const std::filesystem::path& path)
19 : m_lines(std::make_shared<container_t>())
20 {
21 std::ifstream ifs(path);
22 std::string line;
24 while (std::getline(ifs, line)) {
25 m_lines->emplace_back(line);
26 }
27 }
29 const auto& operator[](std::size_t idx) const
30 {
31 return m_lines->operator[](idx);
32 }
33 auto& operator[](std::size_t idx) { return m_lines->operator[](idx); }
35 auto substr(std::size_t idx, std::size_t pos, std::size_t len) const
36 {
37 return pos < operator[](idx).size() ? operator[](idx).substr(pos, len) : "";
38 }
40 auto size() const { return m_lines->size(); }
42 private:
43 std::shared_ptr<container_t> m_lines;
44 };
46 class PanelWindow : public display::Window
47 {
48 public:
49 struct state_t
50 {
51 File file;
52 display::pos_t line;
53 display::pos_t cursor;
54 };
56 PanelWindow(display::plc_t aplc, display::pad_t pad, state_t& state)
57 : Window(aplc, pad)
58 , m_state(state)
59 {
60 }
62 protected:
63 const auto& file() const { return m_state.file; }
64 auto& file() { return m_state.file; }
66 const auto& cursor() const { return m_state.cursor; }
67 auto& cursor() { return m_state.cursor; }
69 const auto& line() const { return m_state.line; }
70 auto& line() { return m_state.line; }
72 void cursor_clamp_width(display::xpos_t& xpos) const
73 {
74 using display::clamp_high, display::xpos_t;
76 const auto& sel = file()[(line().y + cursor().y).value()];
77 const auto size = sub_lim(xpos_t(sel.size()), line().x, xpos_t(0));
79 if (size == 0) {
80 xpos = xpos_t(0);
81 } else {
82 xpos = clamp_high(xpos, size - 1);
83 }
84 }
86 private:
87 state_t& m_state; // NOLINT
88 };
90 class LineNum : public PanelWindow
91 {
92 public:
93 explicit LineNum(display::plc_t aplc, PanelWindow::state_t& state)
94 : PanelWindow(aplc, display::pad_t(0, 0, 1, 0), state)
95 {
96 }
98 void render() const override
99 {
100 line_reset();
102 std::size_t pos = line().y.value();
103 for (std::size_t i = 0; i < hgt(); i++, pos++) {
104 if (pos < file().size()) {
105 line_right(std::to_string(pos));
106 } else {
107 line_right("~");
108 }
109 }
110 }
111 };
113 class LineInfo : public PanelWindow
114 {
115 public:
116 explicit LineInfo(display::plc_t aplc, PanelWindow::state_t& state)
117 : PanelWindow(aplc, display::pad_t(0, 0), state)
118 {
119 }
121 void render() const override
122 {
123 auto crs = cursor();
124 cursor_clamp_width(crs.x);
126 line_reset();
127 line_right(std::to_string(crs.x.value()) + ","
128 + std::to_string((line().y + crs.y).value()));
129 }
131 private:
132 };
134 class Editor : public PanelWindow
135 {
136 public:
137 Editor(display::plc_t aplc, PanelWindow::state_t& state)
138 : PanelWindow(aplc, {0, 0}, state)
139 {
140 }
142 void render() const override
143 {
144 line_reset();
146 auto crs = cursor();
147 auto lin = line();
149 cursor_clamp_width(crs.x);
151 const auto hwidth = (wth() / 2).value();
152 const auto overshoot = crs.x / hwidth;
154 if (overshoot > 1) {
155 lin.x += (overshoot - 1) * hwidth;
156 crs.x -= (overshoot - 1) * hwidth;
157 }
159 for (std::size_t i = 0; i < hgt(); i++, lin.y++) {
160 if (lin.y >= file().size()) {
161 break;
162 }
164 if (i == crs.y) {
165 std::cout << alec::background_v<alec::Color::BLACK>;
166 }
168 line_left(file().substr(lin.y.value(), lin.x.value(), wth().value()));
170 if (i == crs.y) {
171 std::cout << alec::background_v<alec::Color::DEFAULT>;
172 }
173 }
175 Window::render();
177 std::cout << alec::cursor_show_v;
178 set_cursor(crs);
179 std::cout << std::flush;
180 }
182 void input(display::event& evnt) override
183 {
184 using display::event;
186 auto& crs = cursor();
187 auto& lin = line();
189 if (evnt.type() != event::Type::KEY) {
190 return;
191 }
193 if (evnt.key() == 'j') {
194 if (crs.y + 1 < hgt().value()) {
195 crs.y++;
196 } else if (lin.y + hgt() < file().size()) {
197 lin.y++;
198 }
200 evnt.type() = event::Type::NONE;
201 return;
202 }
204 if (evnt.key() == 'k') {
205 if (crs.y > 0) {
206 crs.y--;
207 } else if (lin.y > 0) {
208 lin.y--;
209 }
211 evnt.type() = event::Type::NONE;
212 return;
213 }
215 if (evnt.key() == 'l') {
216 using display::xpos_t;
218 cursor_clamp_width(crs.x);
220 const auto& sel = file()[(lin.y + crs.y).value()];
221 const auto size = sub_lim(xpos_t(sel.size()), lin.x, xpos_t(0));
223 if (crs.x.value() + 1 < size) {
224 crs.x++;
225 }
227 evnt.type() = event::Type::NONE;
228 return;
229 }
231 if (evnt.key() == 'h') {
232 using display::xpos_t;
234 cursor_clamp_width(crs.x);
236 if (crs.x > 0) {
237 crs.x--;
238 }
240 evnt.type() = event::Type::NONE;
241 return;
242 }
243 }
244 };
246 class Panel : public display::Element
247 {
248 public:
249 explicit Panel(display::plc_t aplc, File file)
250 : Element(aplc)
251 , m_state(std::move(file), {0, 0}, {0, 0})
252 , m_num(place_num(), m_state)
253 , m_info(place_info(), m_state)
254 , m_editor(place_editor(), m_state)
255 {
256 }
258 Panel(display::plc_t aplc, const Panel& panel)
259 : Panel(aplc, panel.m_state.file)
260 {
261 }
263 void resize(display::plc_t aplc) override
264 {
265 Element::resize(aplc);
267 m_num.resize(place_num());
268 m_info.resize(place_info());
269 m_editor.resize(place_editor());
270 }
272 void render() const override
273 {
274 m_num.render();
275 m_info.render();
276 m_editor.render();
277 }
279 void input(display::event& evnt) override
280 {
281 m_editor.input(evnt);
283 if (evnt.type() == display::event::Type::NONE) {
284 render();
285 }
286 }
288 void clear() const override
289 {
290 m_num.clear();
291 m_editor.clear();
292 m_info.clear();
293 }
295 private:
296 display::plc_t place_num() const
297 {
298 return {apos() + display::pos_t(0, 0),
299 display::dim_t(get_linenuwidth(), (ahgt() - 1).value())};
300 }
302 display::plc_t place_info() const
303 {
304 return {apos() + display::pos_t(0U, (ahgt() - 1).value()),
305 display::dim_t(awth(), display::hgt_t(1))};
306 }
308 display::plc_t place_editor() const
309 {
310 return {apos() + display::pos_t(get_linenuwidth(), 0U),
311 display::dim_t(awth() - get_linenuwidth(), ahgt() - 1)};
312 }
314 std::uint16_t get_linenuwidth() const
315 {
316 auto number = m_state.file.size();
317 std::uint16_t digits = 1;
319 do {
320 digits++;
321 } while ((number /= 10) > 0);
323 return digits;
324 }
326 PanelWindow::state_t m_state;
328 LineNum m_num;
329 LineInfo m_info;
330 Editor m_editor;
331 };
333 int main(const int argc, const char* argv[])
334 {
335 const std::span args(argv, argv + argc);
337 if (args.size() < 2) {
338 std::cout << "Usage: " << args[0] << " filename\n";
339 return -1;
340 }
342 using editor_t = display::LayoutDynamic<Panel>;
344 auto& inst = display::Display::display();
345 auto& layout = inst.layout().emplace_child<editor_t>();
346 layout.emplace_child(File(args[1]));
348 inst.render();
349 while (true) {
350 using display::event;
352 auto evnt = inst.get_event();
354 if (evnt.type() == event::Type::RESIZE) {
355 std::cout << alec::erase_display_v<alec::Motion::WHOLE>;
356 inst.render();
357 continue;
358 }
360 if (evnt.type() == event::Type::KEY && evnt.key() == 'q') {
361 break;
362 }
364 inst.input(evnt);
366 if (layout.is_finished()) {
367 break;
368 }
369 }
371 return 0;
372 }