#include "tui/tui.hpp" #include "tui/tui_helpers.hpp" #include #include #include void Tui::CompleteCommand(size_t start) { std::string current = input.substr(start); std::vector matches; for (const auto &kv : commands) if (kv.first.rfind(current, 0) == 0) matches.push_back(kv.first); if (matches.empty()) return; auto replace_with = [&](const std::string &replacement) { input.replace(start, std::string::npos, replacement); cursor_pos = (int)input.size(); }; if (matches.size() == 1) { replace_with(matches[0]); return; } std::string lcp = LongestCommonPrefix(matches); if (lcp.size() > current.size()) { replace_with(lcp); return; } std::string line = " "; for (const auto &m : matches) line += " " + m; Print(line); } void Tui::CompletePath(size_t start) { namespace fs = std::filesystem; std::string current = input.substr(start); auto pos = current.rfind('/'); std::string disp, prefix; if (pos == std::string::npos) { disp = ""; prefix = current; } else { disp = current.substr(0, pos + 1); prefix = current.substr(pos + 1); } std::string resolved = disp.empty() ? "." : disp; if (!resolved.empty() && resolved[0] == '~') { if (const char *home = std::getenv("HOME")) resolved = std::string(home) + resolved.substr(1); } std::vector names; std::vector is_dir; try { for (const auto &e : fs::directory_iterator(resolved)) { std::string n = e.path().filename().string(); if (n.rfind(prefix, 0) == 0) { names.push_back(n); is_dir.push_back(e.is_directory()); } } } catch (const std::exception &) { return; } if (names.empty()) return; auto replace_with = [&](const std::string &replacement) { input.replace(start, std::string::npos, replacement); cursor_pos = (int)input.size(); }; if (names.size() == 1) { replace_with(disp + names[0] + (is_dir[0] ? "/" : "")); return; } std::string lcp = LongestCommonPrefix(names); if (lcp.size() > prefix.size()) { replace_with(disp + lcp); return; } std::string line = " "; for (size_t i = 0; i < names.size(); ++i) line += " " + names[i] + (is_dir[i] ? "/" : ""); Print(line); } void Tui::CompleteInline() { bool ends_with_ws = !input.empty() && std::isspace((unsigned char)input.back()); size_t arg_start; if (input.empty() || ends_with_ws) { arg_start = input.size(); } else { size_t i = input.size(); while (i > 0 && !std::isspace((unsigned char)input[i - 1])) --i; arg_start = i; } auto preceding = Tokenize(input.substr(0, arg_start)); int arg_index = (int)preceding.size(); if (arg_index == 0) { CompleteCommand(); return; } if (preceding.empty()) return; auto cmd_it = commands.find(preceding[0]); if (cmd_it == commands.end()) return; const auto &spec = cmd_it->second; int param_idx = arg_index - 1; if (param_idx >= (int)spec.params.size()) return; switch (spec.params[param_idx].completion) { case Completion::Path: CompletePath(arg_start); break; case Completion::Command: CompleteCommand(arg_start); break; case Completion::None: break; } }