Panini 1.4.0
Header-only library for generating C++, written in C++17
DebugWriter.hpp
Go to the documentation of this file.
1/*
2 MIT No Attribution
3
4 Copyright 2021-2023 Mr. Hands
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to
8 deal in the Software without restriction, including without limitation the
9 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 sell copies of the Software, and to permit persons to whom the Software is
11 furnished to do so.
12
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 DEALINGS IN THE SOFTWARE.
20*/
21
22#pragma once
23
24#ifdef _WINDOWS
25 #ifndef WIN32_LEAN_AND_MEAN
26 #define WIN32_LEAN_AND_MEAN
27 #endif
28
29 #ifndef NOMINMAX
30 #define NOMINMAX
31 #endif
32
33 #include <Windows.h>
34#endif
35
37#include "writers/Writer.hpp"
38
39namespace panini
40{
41
60 : public ConfiguredWriter<DebugWriterConfig>
61 {
62
63 public:
67 struct Colors
68 {
69 enum Values : uint16_t
70 {
71 Black = 0,
72 Blue = (1 << 0),
73 Green = (1 << 1),
74 Red = (1 << 2),
79 Light = (1 << 3),
80 };
81 };
82
88 inline explicit DebugWriter(
89 const DebugWriterConfig& config = DebugWriterConfig{})
90 : ConfiguredWriter(config)
91 , m_debugConfig(config)
92 {
93 #ifdef _WINDOWS
94 m_output = ::GetStdHandle(STD_OUTPUT_HANDLE);
95 m_initialized = ::GetConsoleScreenBufferInfo(m_output, &m_screenInfo);
96
97 m_consoleWidth = static_cast<int32_t>(m_screenInfo.dwSize.X);
98 m_consoleHeight = static_cast<int32_t>(m_screenInfo.dwSize.Y);
99
100 const DWORD screenSize = m_screenInfo.dwSize.X * m_screenInfo.dwSize.Y;
101
102 // clear the screen with spaces
103
104 DWORD written = 0;
105 ::FillConsoleOutputCharacterA(
106 m_output,
107 ' ',
108 screenSize,
109 m_cursor,
110 &written
111 );
112
113 // clear background formatting
114
115 ::FillConsoleOutputAttribute(
116 m_output,
117 m_screenInfo.wAttributes,
118 screenSize,
119 m_cursor,
120 &written
121 );
122 #endif
123
124 std::cout.flush();
125
126 // reset cursor
127
128 SetCursorPosition(0, 0);
129 }
130
134 inline ~DebugWriter() override
135 {
136 Commit();
137 }
138
142 inline bool IsChanged() const override
143 {
144 return true;
145 }
146
147 protected:
151 inline void Write(const std::string& chunk) override
152 {
153 if (!m_initialized)
154 {
155 return;
156 }
157
158 // line numbers
159
160 if (IsOnNewLine())
161 {
162 std::string padded = std::to_string(m_cursorY + 1);
163 for (size_t i = padded.length(); i < m_debugConfig.lineNumberPadding; ++i)
164 {
165 padded.insert(padded.begin(), ' ');
166 }
167
169 WriteChunk(padded + " ");
170 ResetStyles();
171 }
172
173 const std::string& indentStr = GetConfig().chunkIndent;
174
175 // indentation
176
177 if (chunk.substr(0, indentStr.length()) == indentStr)
178 {
179 size_t offset = 0;
180 int32_t count = 0;
181
182 while (chunk.substr(offset, indentStr.length()) == indentStr)
183 {
184 SetColor(
185 (count % 2 == 0) ? Colors::Yellow : Colors::Cyan,
187 );
188 WriteChunk("-> ");
189
190 offset += indentStr.length();
191 count++;
192 }
193
194 ResetStyles();
195 }
196
197 // other chunks
198
199 else
200 {
201 WriteChunk(chunk);
202 }
203 }
204
205 inline void WriteNewLine() override
206 {
208 WriteChunk("<LF>");
209 ResetStyles();
210
212
213 // handle input
214
215 if (m_isDebugging)
216 {
217 HandleInput("Press <Enter> to continue, <Q> to quit debugging");
218 }
219 }
220
221 inline bool OnCommit(bool force = false) override
222 {
223 (void)force;
224
225 if (m_initialized)
226 {
227 HandleInput("<END>");
228
229 m_initialized = false;
230 }
231
233
234 return true;
235 }
236
240 inline void SetCursorPosition(int32_t x, int32_t y)
241 {
242 m_cursorX = x;
243 m_cursorY = y;
244
245 #ifdef _WINDOWS
246 m_cursor.X = static_cast<SHORT>(m_cursorX);
247 m_cursor.Y = static_cast<SHORT>(m_cursorY);
248 ::SetConsoleCursorPosition(m_output, m_cursor);
249 #endif
250 }
251
255 inline void SetColor(uint16_t background, uint16_t foreground)
256 {
257 #ifdef _WINDOWS
258 ::SetConsoleTextAttribute(m_output, (background << 4) | foreground);
259 #else
260 (void)background;
261 (void)foreground;
262 #endif
263 }
264
268 inline void ResetStyles()
269 {
271 }
272
277 inline void WriteChunk(const std::string& chunk)
278 {
279 size_t offsetX = static_cast<size_t>(m_cursorX) + chunk.length();
280 size_t maxWidth = static_cast<size_t>(m_consoleWidth);
281
282 if (offsetX > maxWidth)
283 {
284 WriteChunk(chunk.substr(0, maxWidth - chunk.length()));
285
287 WriteChunk(chunk.substr(maxWidth - chunk.length()));
288
289 return;
290 }
291
292 std::cout.flush();
293 std::cout << chunk;
294
296 m_cursorX + static_cast<int32_t>(chunk.length()),
298 );
299 }
300
304 inline void HandleInput(const std::string& message)
305 {
308 std::cout << message;
309 ResetStyles();
310
311 // wait for input
312
313 #ifdef _WINDOWS
314 HANDLE input = ::GetStdHandle(STD_INPUT_HANDLE);
315
316 while (1)
317 {
318 INPUT_RECORD record;
319 DWORD read = 0;
320 ::ReadConsoleInputA(input, &record, 1, &read);
321 if (record.EventType == KEY_EVENT)
322 {
323 KEY_EVENT_RECORD& keyEvent = record.Event.KeyEvent;
324
325 // wait for the key to be released
326
327 if (keyEvent.bKeyDown)
328 {
329 continue;
330 }
331
332 // stop debugging when Q is pressed
333
334 m_isDebugging = keyEvent.wVirtualKeyCode != 'Q';
335
336 break;
337 }
338 }
339 #endif
340
341 // clear line
342
343 #ifdef _WINDOWS
344 DWORD written = 0;
345 ::FillConsoleOutputCharacterA(
346 m_output,
347 ' ',
348 static_cast<DWORD>(message.length()),
349 m_cursor,
350 &written
351 );
352
353 ::FillConsoleOutputAttribute(
354 m_output,
355 m_screenInfo.wAttributes,
356 static_cast<DWORD>(message.length()),
357 m_cursor,
358 &written
359 );
360 #endif
361
362 // reset cursor
363
365 }
366
367 protected:
369 bool m_initialized = false;
370 bool m_isDebugging = true;
371 int32_t m_cursorX = 0;
372 int32_t m_cursorY = 0;
373 int32_t m_consoleWidth = 0;
374 int32_t m_consoleHeight = 0;
375
376 #ifdef _WINDOWS
377 HANDLE m_output;
378 CONSOLE_SCREEN_BUFFER_INFO m_screenInfo;
379 COORD m_cursor = { 0, 0 };
380 #endif
381
382 };
383
384};
Base class implementation for writers.
Definition: Writer.hpp:185
bool Commit(bool force=false) override
Definition: Writer.hpp:453
const WriterConfig & GetConfig() const override
Definition: Writer.hpp:224
ConfiguredWriter(const DebugWriterConfig &config=DebugWriterConfig {})
Definition: Writer.hpp:193
bool IsOnNewLine() const override
Definition: Writer.hpp:261
Writes output to the console line-by-line.
Definition: DebugWriter.hpp:61
void SetCursorPosition(int32_t x, int32_t y)
Definition: DebugWriter.hpp:240
bool IsChanged() const override
Definition: DebugWriter.hpp:142
int32_t m_consoleWidth
Definition: DebugWriter.hpp:373
void WriteChunk(const std::string &chunk)
Definition: DebugWriter.hpp:277
void ResetStyles()
Definition: DebugWriter.hpp:268
int32_t m_consoleHeight
Definition: DebugWriter.hpp:374
void WriteNewLine() override
Definition: DebugWriter.hpp:205
bool OnCommit(bool force=false) override
Definition: DebugWriter.hpp:221
bool m_isDebugging
Definition: DebugWriter.hpp:370
int32_t m_cursorY
Definition: DebugWriter.hpp:372
void Write(const std::string &chunk) override
Definition: DebugWriter.hpp:151
DebugWriter(const DebugWriterConfig &config=DebugWriterConfig{})
Definition: DebugWriter.hpp:88
~DebugWriter() override
Definition: DebugWriter.hpp:134
DebugWriterConfig m_debugConfig
Definition: DebugWriter.hpp:368
bool m_initialized
Definition: DebugWriter.hpp:369
void SetColor(uint16_t background, uint16_t foreground)
Definition: DebugWriter.hpp:255
void HandleInput(const std::string &message)
Definition: DebugWriter.hpp:304
int32_t m_cursorX
Definition: DebugWriter.hpp:371
Definition: Braces.hpp:29
Colors used in the console output.
Definition: DebugWriter.hpp:68
Values
Definition: DebugWriter.hpp:70
@ Green
Definition: DebugWriter.hpp:73
@ Red
Definition: DebugWriter.hpp:74
@ Black
Definition: DebugWriter.hpp:71
@ Yellow
Definition: DebugWriter.hpp:75
@ Cyan
Definition: DebugWriter.hpp:76
@ Fuchsia
Definition: DebugWriter.hpp:77
@ White
Definition: DebugWriter.hpp:78
@ Blue
Definition: DebugWriter.hpp:72
@ Light
Definition: DebugWriter.hpp:79
Configuration for the DebugWriter class.
Definition: DebugWriterConfig.hpp:37
size_t lineNumberPadding
Definition: DebugWriterConfig.hpp:41
std::string chunkIndent
Definition: WriterConfig.hpp:58