Compare commits
	
		
			2 Commits
		
	
	
		
			c09929d2f7
			...
			40d98cdbb6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 40d98cdbb6 | |||
| 28613af006 | 
| @@ -7,6 +7,10 @@ add_executable(${PROJECT_NAME} | |||||||
|         Page.cpp |         Page.cpp | ||||||
|         stringutil.hpp |         stringutil.hpp | ||||||
|         stringutil.cpp |         stringutil.cpp | ||||||
|  |         Tui.cpp | ||||||
|  |         Tui.h | ||||||
|  |         Command.h | ||||||
|  |         Command.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| target_link_libraries(${PROJECT_NAME} PRIVATE LibXml2::LibXml2 cpr::cpr) | target_link_libraries(${PROJECT_NAME} PRIVATE LibXml2::LibXml2 cpr::cpr) | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								src/Command.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/Command.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | #include "Command.h" | ||||||
|  |  | ||||||
|  | #include <iostream> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | namespace Command { | ||||||
|  |     Command readCommand() { | ||||||
|  |         if (!std::cin) | ||||||
|  |             return {Exit{}}; | ||||||
|  |         std::string line; | ||||||
|  |         if (!std::getline(std::cin, line)) | ||||||
|  |             return {Exit{}}; | ||||||
|  |  | ||||||
|  |         // Trim leading and trailing whitespaces | ||||||
|  |         constexpr std::string_view TO_TRIM = " \t\n\r"; | ||||||
|  |         const size_t start = line.find_first_not_of(TO_TRIM); | ||||||
|  |         const size_t end = line.find_last_not_of(TO_TRIM); | ||||||
|  |         if (start == std::string::npos) return None{}; | ||||||
|  |         const std::string_view line_trimmed(line.data() + start, end - start + 1); | ||||||
|  |  | ||||||
|  |         using namespace std::string_view_literals; | ||||||
|  |         if (line_trimmed == "r"sv || line_trimmed == "refresh"sv) | ||||||
|  |             return Refresh{}; | ||||||
|  |         if (line_trimmed == "n"sv || line_trimmed == ">"sv || line_trimmed == "next"sv) | ||||||
|  |             return Next{}; | ||||||
|  |         if (line_trimmed == "p"sv || line_trimmed == "<"sv || line_trimmed == "previous"sv) | ||||||
|  |             return Previous{}; | ||||||
|  |         if (line_trimmed == "x"sv || line_trimmed == "exit"sv || line_trimmed == "q"sv || line_trimmed == "quit"sv) | ||||||
|  |             return Exit{}; | ||||||
|  |         if (line_trimmed == "help"sv || line_trimmed == "?"sv || line_trimmed == "?"sv) | ||||||
|  |             return Help{}; | ||||||
|  |  | ||||||
|  |         if (line_trimmed.starts_with("seek") || line.starts_with("s ")) { | ||||||
|  |             const size_t space = line_trimmed.find(' '); | ||||||
|  |             if (space == std::string::npos) | ||||||
|  |                 throw NoNumberException(); | ||||||
|  |             int number = 0; | ||||||
|  |             const size_t size = line.size(); | ||||||
|  |             for (size_t i = space + 1; i < size; ++i) { | ||||||
|  |                 const char c = line_trimmed[i]; | ||||||
|  |                 if (!std::isdigit(c)) | ||||||
|  |                     throw NoNumberException(); | ||||||
|  |                 number = number * 10 + (c - '0'); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return {Seek{number}}; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return {None{}}; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								src/Command.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/Command.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | namespace Command { | ||||||
|  |     class NoNumberException final : public std::exception { | ||||||
|  |     public: | ||||||
|  |         [[nodiscard]] const char *what() const noexcept override { | ||||||
|  |             return "Seek option was used without a number"; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     struct Help { | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct None { | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct Next { | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct Previous { | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct Refresh { | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct Exit { | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct Seek { | ||||||
|  |         int number; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     using Command = std::variant<None, Help, Next, Previous, Refresh, Exit, Seek>; | ||||||
|  |  | ||||||
|  |     Command readCommand(); | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								src/Page.cpp
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/Page.cpp
									
									
									
									
									
								
							| @@ -19,45 +19,44 @@ | |||||||
| #include "libxml/xpath.h" | #include "libxml/xpath.h" | ||||||
|  |  | ||||||
|  |  | ||||||
| bool is_number(std::string_view s){ | bool is_number(const std::string_view s) { | ||||||
|     return std::all_of(s.begin(), s.end(), [](unsigned char c ){ |     return std::ranges::all_of(s, [](const unsigned char c) { | ||||||
|         return std::isdigit(c); |         return std::isdigit(c); | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
| void italize_numbers(std::string &content, size_t leave_chars = 0) { | void italize_numbers(std::string &content, size_t leave_chars = 0) { | ||||||
|     size_t end = content.size(); |     size_t end = content.size(); | ||||||
|      |  | ||||||
|     // Process backwards, word by word |     // Process backwards, word by word | ||||||
|     for (;;) { |     for (;;) { | ||||||
|         size_t space = content.rfind(' ', end - 1); |         const size_t space = content.rfind(' ', end - 1); | ||||||
|         size_t begin = (space == std::string::npos) ? 0 : space + 1; |         const size_t begin = (space == std::string::npos) ? 0 : space + 1; | ||||||
|         size_t word_length = end - begin; |         const size_t word_length = end - begin; | ||||||
|          |  | ||||||
|         if (is_number(content.substr(begin, word_length))) { |         if (is_number(content.substr(begin, word_length))) { | ||||||
|             content.insert(end, ansi::CLEAR); |             content.insert(end, ansi::CLEAR); | ||||||
|             content.insert(begin, ansi::ITALIC); |             content.insert(begin, ansi::ITALIC); | ||||||
|         } |         } | ||||||
|          |  | ||||||
|         if (space == std::string::npos) |         if (space == std::string::npos) | ||||||
|             break; |             break; | ||||||
|         if (leave_chars >= space) |         if (leave_chars >= space) | ||||||
|             break; |             break; | ||||||
|          |  | ||||||
|         end = space; |         end = space; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void pretty_format_page(std::string &content) { | void pretty_format_page(std::string &content) { | ||||||
|     content.insert(0, ansi::BOLD); |     content.insert(0, ansi::BOLD); | ||||||
|     size_t line_end = content.find("\n"); |     const size_t line_end = content.find("\n"); | ||||||
|     content.insert(line_end, ansi::CLEAR); |     content.insert(line_end, ansi::CLEAR); | ||||||
|  |  | ||||||
|     italize_numbers(content, line_end + ansi::CLEAR.size()); |     italize_numbers(content, line_end + ansi::CLEAR.size()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Page::Page(const uint_fast8_t number): m_number(number), m_subpage(fetchSubpage()) { | Page::Page(const uint_fast8_t number): m_number(number), m_subpage(fetchSubpage()) { | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -75,7 +74,7 @@ Page &Page::operator-=(int) { | |||||||
|     return *this; |     return *this; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::string Page::str() const { | std::string Page::str_pretty() const { | ||||||
|     std::string ret; |     std::string ret; | ||||||
|  |  | ||||||
|     std::istringstream stream(m_subpage); |     std::istringstream stream(m_subpage); | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ static constexpr uint_fast8_t MAX_WHITESPACE = 2; | |||||||
| class Page | class Page | ||||||
| { | { | ||||||
| private: | private: | ||||||
|     uint_fast8_t m_number{}; |     uint_fast8_t m_number; | ||||||
|     std::string m_subpage; |     std::string m_subpage; | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -32,7 +32,7 @@ public: | |||||||
|  |  | ||||||
|     Page &operator-=(int); |     Page &operator-=(int); | ||||||
|  |  | ||||||
|     std::string str() const; |     [[nodiscard]] std::string str_pretty() const; | ||||||
|  |  | ||||||
|     bool refresh(); |     bool refresh(); | ||||||
|  |  | ||||||
| @@ -40,6 +40,4 @@ private: | |||||||
|     [[nodiscard]] std::string url() const; |     [[nodiscard]] std::string url() const; | ||||||
|  |  | ||||||
|     [[nodiscard]] std::string fetchSubpage() const; |     [[nodiscard]] std::string fetchSubpage() const; | ||||||
|  |  | ||||||
|     [[nodiscard]] bool contentEquals(const std::vector<std::string> &subpagesOther) const; |  | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								src/Tui.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/Tui.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | #include "Tui.h" | ||||||
|  |  | ||||||
|  | #include <iostream> | ||||||
|  | #include <optional> | ||||||
|  | #include <ostream> | ||||||
|  | #include <utility> | ||||||
|  |  | ||||||
|  | #include "Command.h" | ||||||
|  |  | ||||||
|  | Tui::Tui(Page page) : m_page(std::move(page)) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | std::optional<int> get_page_number(const std::string_view line) { | ||||||
|  |     const size_t space = line.find_first_of(' '); | ||||||
|  |     if (space == std::string::npos) | ||||||
|  |         return std::nullopt; | ||||||
|  |  | ||||||
|  |     int number = 0; | ||||||
|  |     const size_t size = line.size(); | ||||||
|  |     for (size_t i = space; i < size; i++) { | ||||||
|  |         const char c = line[i]; | ||||||
|  |         if (!std::isdigit(c)) | ||||||
|  |             return std::nullopt; | ||||||
|  |  | ||||||
|  |         number = number * 10 + (c - '0'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return number; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void printHelp() { | ||||||
|  |     std::cout | ||||||
|  |             << "Available Commands:\n" | ||||||
|  |             << "  r, refresh                : Refresh the current view.\n" | ||||||
|  |             << "  n, >, next                : Move to the next item.\n" | ||||||
|  |             << "  p, <, previous            : Move to the previous item.\n" | ||||||
|  |             << "  x, exit, q, quit          : Exit the application.\n" | ||||||
|  |             << "  seek <number>, s <number> : Seek to the specified number.\n" | ||||||
|  |             << "  help, h, ?                : Display this help message." | ||||||
|  |             << std::endl; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Tui::run() { | ||||||
|  |     std::cout << m_page.str_pretty() << std::endl; | ||||||
|  |     for (;;) { | ||||||
|  |         std::cout << "Enter a command: "; | ||||||
|  |         std::flush(std::cout); | ||||||
|  |         const Command::Command command = Command::readCommand(); | ||||||
|  |  | ||||||
|  |         bool should_exit = false; | ||||||
|  |         auto visitor = [this, &should_exit]<typename T0>(T0 &&arg) { | ||||||
|  |             using T = std::decay_t<T0>; | ||||||
|  |  | ||||||
|  |             if constexpr (std::is_same_v<T, Command::None>) { | ||||||
|  |                 std::cout << "Invalid command!" << std::endl; | ||||||
|  |             } else if constexpr (std::is_same_v<T, Command::Next>) { | ||||||
|  |                 m_page += 1; | ||||||
|  |                 std::cout << m_page.str_pretty() << std::endl; | ||||||
|  |             } else if constexpr (std::is_same_v<T, Command::Previous>) { | ||||||
|  |                 m_page -= 1; | ||||||
|  |                 std::cout << m_page.str_pretty() << std::endl; | ||||||
|  |             } else if constexpr (std::is_same_v<T, Command::Refresh>) { | ||||||
|  |                 m_page.refresh(); | ||||||
|  |                 std::cout << m_page.str_pretty() << std::endl; | ||||||
|  |             } else if constexpr (std::is_same_v<T, Command::Exit>) { | ||||||
|  |                 should_exit = true; | ||||||
|  |             } else if constexpr (std::is_same_v<T, Command::Help>) { | ||||||
|  |                 printHelp(); | ||||||
|  |             } else if constexpr (std::is_same_v<T, Command::Seek>) { | ||||||
|  |                 const Command::Seek &seek = arg; | ||||||
|  |                 const int number = seek.number; | ||||||
|  |                 m_page = Page(number); | ||||||
|  |                 std::cout << m_page.str_pretty() << std::endl; | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         std::visit(visitor, command); | ||||||
|  |         if (should_exit) | ||||||
|  |             return; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								src/Tui.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/Tui.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "Page.hpp" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Tui { | ||||||
|  | public: | ||||||
|  |     Tui(Page page); | ||||||
|  |  | ||||||
|  |     void run(); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     Page m_page; | ||||||
|  | }; | ||||||
| @@ -1,8 +1,7 @@ | |||||||
| #include <iostream> |  | ||||||
|  |  | ||||||
| #include "Page.hpp" | #include "Page.hpp" | ||||||
|  | #include "Tui.h" | ||||||
|  |  | ||||||
| int main(int argc, char *argv[]) { | int main(int argc, char *argv[]) { | ||||||
|     const auto page = Page(); |     Tui tui = Page(); | ||||||
|     std::cout << page.str() << std::endl; |     tui.run(); | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user