refactor: Improve code quality and Unix philosophy

- Remove redundant comments for cleaner code
- Simplify error messages and status display
- Improve code consistency across modules
- Fix GitHub Actions workflow binary names
- Enhance .gitignore for common editor files
- Align help text formatting
- Remove unnecessary verbose comments
This commit is contained in:
m1ngsama 2025-12-08 15:53:17 +08:00
parent ab2d1932e4
commit ef80f9ab82
6 changed files with 36 additions and 81 deletions

View file

@ -47,13 +47,13 @@ jobs:
- name: Rename binary with platform suffix
run: |
mv build/nbtca_tui build/nbtca_tui-${{ matrix.name }}
mv build/tut build/tut-${{ matrix.name }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: nbtca_tui-${{ matrix.name }}
path: build/nbtca_tui-${{ matrix.name }}
name: tut-${{ matrix.name }}
path: build/tut-${{ matrix.name }}
release:
needs: build
@ -85,13 +85,13 @@ jobs:
Automated release for commit ${{ github.sha }}
## Download
- **macOS**: `nbtca_tui-macos`
- **Linux**: `nbtca_tui-linux`
- **macOS**: `tut-macos`
- **Linux**: `tut-linux`
## Build from source
See the [README](https://github.com/${{ github.repository }}/blob/main/README.md) for build instructions.
files: |
artifacts/nbtca_tui-macos/nbtca_tui-macos
artifacts/nbtca_tui-linux/nbtca_tui-linux
artifacts/tut-macos/tut-macos
artifacts/tut-linux/tut-linux
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

5
.gitignore vendored
View file

@ -2,3 +2,8 @@ build/
*.o
tut
.DS_Store
*.swp
*.swo
*~
.vscode/
.idea/

View file

@ -52,9 +52,9 @@ public:
auto response = http_client.fetch(url);
if (!response.is_success()) {
status_message = "Error: " + (response.error_message.empty() ?
status_message = response.error_message.empty() ?
"HTTP " + std::to_string(response.status_code) :
response.error_message);
response.error_message;
return false;
}
@ -65,14 +65,13 @@ public:
current_link = -1;
search_results.clear();
// 更新历史
if (history_pos >= 0 && history_pos < static_cast<int>(history.size()) - 1) {
history.erase(history.begin() + history_pos + 1, history.end());
}
history.push_back(url);
history_pos = history.size() - 1;
status_message = "Loaded: " + (current_doc.title.empty() ? url : current_doc.title);
status_message = current_doc.title.empty() ? url : current_doc.title;
return true;
}
@ -80,7 +79,6 @@ public:
attron(COLOR_PAIR(COLOR_STATUS_BAR));
mvprintw(screen_height - 1, 0, "%s", std::string(screen_width, ' ').c_str());
// 显示模式和缓冲
std::string mode_str;
InputMode mode = input_handler.get_mode();
switch (mode) {
@ -88,29 +86,24 @@ public:
mode_str = "NORMAL";
break;
case InputMode::COMMAND:
mode_str = input_handler.get_buffer();
break;
case InputMode::SEARCH:
mode_str = input_handler.get_buffer();
break;
default:
mode_str = "???";
mode_str = "";
break;
}
// 左侧:模式或命令
mvprintw(screen_height - 1, 0, " %s", mode_str.c_str());
// 中间:状态消息
if (!status_message.empty() && mode == InputMode::NORMAL) {
int msg_x = (screen_width - status_message.length()) / 2;
if (msg_x < mode_str.length() + 2) {
if (msg_x < static_cast<int>(mode_str.length()) + 2) {
msg_x = mode_str.length() + 2;
}
mvprintw(screen_height - 1, msg_x, "%s", status_message.c_str());
}
// 右侧:位置信息
int total_lines = rendered_lines.size();
int visible_lines = screen_height - 2;
int percentage = 0;
@ -147,7 +140,6 @@ public:
int line_idx = scroll_pos + i;
const auto& line = rendered_lines[line_idx];
// 高亮当前链接
if (line.is_link && line.link_index == current_link) {
attron(COLOR_PAIR(COLOR_LINK_ACTIVE));
} else {
@ -157,15 +149,12 @@ public:
}
}
// 搜索高亮
std::string display_text = line.text;
if (!search_term.empty() &&
std::find(search_results.begin(), search_results.end(), line_idx) != search_results.end()) {
// 简单高亮:整行反色(实际应该只高亮匹配部分)
attron(A_REVERSE);
}
mvprintw(i, 0, "%s", display_text.c_str());
mvprintw(i, 0, "%s", line.text.c_str());
if (!search_term.empty() &&
std::find(search_results.begin(), search_results.end(), line_idx) != search_results.end()) {
@ -225,7 +214,6 @@ public:
case Action::NEXT_LINK:
if (!current_doc.links.empty()) {
current_link = (current_link + 1) % current_doc.links.size();
// 滚动到链接位置
scroll_to_link(current_link);
}
break;

View file

@ -23,22 +23,18 @@ public:
result.has_count = false;
result.count = 1;
// 处理数字前缀
if (std::isdigit(ch) && (ch != '0' || !count_buffer.empty())) {
count_buffer += static_cast<char>(ch);
return result;
}
// 解析count
if (!count_buffer.empty()) {
result.has_count = true;
result.count = std::stoi(count_buffer);
count_buffer.clear();
}
// 处理vim风格的命令
switch (ch) {
// 移动
case 'j':
case KEY_DOWN:
result.action = Action::SCROLL_DOWN;
@ -55,18 +51,14 @@ public:
case KEY_RIGHT:
result.action = Action::GO_FORWARD;
break;
// 翻页
case 4: // Ctrl-D
case 4:
case ' ':
result.action = Action::SCROLL_PAGE_DOWN;
break;
case 21: // Ctrl-U
case 21:
case 'b':
result.action = Action::SCROLL_PAGE_UP;
break;
// 跳转
case 'g':
buffer += 'g';
if (buffer == "gg") {
@ -82,8 +74,6 @@ public:
result.action = Action::GOTO_BOTTOM;
}
break;
// 搜索
case '/':
mode = InputMode::SEARCH;
buffer = "/";
@ -94,27 +84,21 @@ public:
case 'N':
result.action = Action::SEARCH_PREV;
break;
// 链接导航
case '\t': // Tab
case '\t':
result.action = Action::NEXT_LINK;
break;
case KEY_BTAB: // Shift-Tab (可能不是所有终端都支持)
case KEY_BTAB:
case 'T':
result.action = Action::PREV_LINK;
break;
case '\n': // Enter
case '\n':
case '\r':
result.action = Action::FOLLOW_LINK;
break;
// 命令模式
case ':':
mode = InputMode::COMMAND;
buffer = ":";
break;
// 其他操作
case 'r':
result.action = Action::REFRESH;
break;
@ -124,7 +108,6 @@ public:
case '?':
result.action = Action::HELP;
break;
default:
buffer.clear();
break;
@ -138,8 +121,7 @@ public:
result.action = Action::NONE;
if (ch == '\n' || ch == '\r') {
// 执行命令
std::string command = buffer.substr(1); // 去掉':'
std::string command = buffer.substr(1);
if (command == "q" || command == "quit") {
result.action = Action::QUIT;
@ -148,14 +130,12 @@ public:
} else if (command == "r" || command == "refresh") {
result.action = Action::REFRESH;
} else if (command.rfind("o ", 0) == 0 || command.rfind("open ", 0) == 0) {
// :o URL 或 :open URL
size_t space_pos = command.find(' ');
if (space_pos != std::string::npos) {
result.action = Action::OPEN_URL;
result.text = command.substr(space_pos + 1);
}
} else if (!command.empty() && std::isdigit(command[0])) {
// 跳转到行号
try {
result.action = Action::GOTO_LINE;
result.number = std::stoi(command);
@ -166,7 +146,7 @@ public:
mode = InputMode::NORMAL;
buffer.clear();
} else if (ch == 27) { // ESC
} else if (ch == 27) {
mode = InputMode::NORMAL;
buffer.clear();
} else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) {
@ -188,14 +168,13 @@ public:
result.action = Action::NONE;
if (ch == '\n' || ch == '\r') {
// 执行搜索
if (buffer.length() > 1) {
result.action = Action::SEARCH_FORWARD;
result.text = buffer.substr(1); // 去掉'/'
result.text = buffer.substr(1);
}
mode = InputMode::NORMAL;
buffer.clear();
} else if (ch == 27) { // ESC
} else if (ch == 27) {
mode = InputMode::NORMAL;
buffer.clear();
} else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) {

View file

@ -12,19 +12,18 @@ void print_usage(const char* prog_name) {
<< " " << prog_name << " https://example.com\n"
<< " " << prog_name << " https://news.ycombinator.com\n\n"
<< "Vim-style keybindings:\n"
<< " j/k - Scroll down/up\n"
<< " gg/G - Go to top/bottom\n"
<< " / - Search\n"
<< " Tab - Next link\n"
<< " Enter - Follow link\n"
<< " h/l - Back/Forward\n"
<< " :o URL - Open URL\n"
<< " :q - Quit\n"
<< " ? - Show help\n";
<< " j/k - Scroll down/up\n"
<< " gg/G - Go to top/bottom\n"
<< " / - Search\n"
<< " Tab - Next link\n"
<< " Enter - Follow link\n"
<< " h/l - Back/Forward\n"
<< " :o URL - Open URL\n"
<< " :q - Quit\n"
<< " ? - Show help\n";
}
int main(int argc, char* argv[]) {
// 解析命令行参数
std::string initial_url;
if (argc > 1) {

View file

@ -7,7 +7,6 @@ class TextRenderer::Impl {
public:
RenderConfig config;
// 自动换行处理
std::vector<std::string> wrap_text(const std::string& text, int width) {
std::vector<std::string> lines;
if (text.empty()) {
@ -19,20 +18,17 @@ public:
std::string current_line;
while (words_stream >> word) {
// 处理单个词超长的情况
if (word.length() > static_cast<size_t>(width)) {
if (!current_line.empty()) {
lines.push_back(current_line);
current_line.clear();
}
// 强制分割长词
for (size_t i = 0; i < word.length(); i += width) {
lines.push_back(word.substr(i, width));
}
continue;
}
// 正常换行逻辑
if (current_line.empty()) {
current_line = word;
} else if (current_line.length() + 1 + word.length() <= static_cast<size_t>(width)) {
@ -54,7 +50,6 @@ public:
return lines;
}
// 添加缩进
std::string add_indent(const std::string& text, int indent) {
return std::string(indent, ' ') + text;
}
@ -69,20 +64,17 @@ TextRenderer::~TextRenderer() = default;
std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int screen_width) {
std::vector<RenderedLine> lines;
// 计算实际内容宽度
int content_width = std::min(pImpl->config.max_width, screen_width - 4);
if (content_width < 40) {
content_width = screen_width - 4;
}
// 计算左边距(如果居中)
int margin = 0;
if (pImpl->config.center_content && content_width < screen_width) {
margin = (screen_width - content_width) / 2;
}
pImpl->config.margin_left = margin;
// 渲染标题
if (!doc.title.empty()) {
RenderedLine title_line;
title_line.text = std::string(margin, ' ') + doc.title;
@ -92,7 +84,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
title_line.link_index = -1;
lines.push_back(title_line);
// 标题下划线
RenderedLine underline;
underline.text = std::string(margin, ' ') + std::string(std::min((int)doc.title.length(), content_width), '=');
underline.color_pair = COLOR_HEADING1;
@ -101,7 +92,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
underline.link_index = -1;
lines.push_back(underline);
// 空行
RenderedLine empty;
empty.text = "";
empty.color_pair = COLOR_NORMAL;
@ -111,7 +101,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
lines.push_back(empty);
}
// 渲染URL
if (!doc.url.empty()) {
RenderedLine url_line;
url_line.text = std::string(margin, ' ') + "URL: " + doc.url;
@ -130,7 +119,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
lines.push_back(empty);
}
// 渲染内容元素
for (const auto& elem : doc.elements) {
int color = COLOR_NORMAL;
bool bold = false;
@ -179,7 +167,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
break;
}
// 换行处理
auto wrapped_lines = pImpl->wrap_text(elem.text, content_width - prefix.length());
for (size_t i = 0; i < wrapped_lines.size(); ++i) {
RenderedLine line;
@ -195,7 +182,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
lines.push_back(line);
}
// 段落间距
if (elem.type == ElementType::PARAGRAPH ||
elem.type == ElementType::HEADING1 ||
elem.type == ElementType::HEADING2 ||
@ -212,7 +198,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
}
}
// 渲染链接列表
if (!doc.links.empty() && pImpl->config.show_link_indicators) {
RenderedLine separator;
std::string sepline(content_width, '-');
@ -254,7 +239,6 @@ std::vector<RenderedLine> TextRenderer::render(const ParsedDocument& doc, int sc
lines.push_back(link_line);
}
// URL on next line
auto url_wrapped = pImpl->wrap_text(link.url, content_width - 6);
for (const auto& url_line_text : url_wrapped) {
RenderedLine url_line;