Files
bantam-cpp/editor.cpp
T
2026-10-14 00:01:03 +03:00

93 lines
3.0 KiB
C++

/* editor.cpp — GNU readline line editor, history and term helpers
* (is_terminal, term_width). The interactive prompt is handled by
* libreadline instead of a hand-rolled raw-mode terminal editor. */
#include "bantam.hpp"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <readline/readline.h>
#include <readline/history.h>
#include <sys/ioctl.h>
#include <unistd.h>
/* ----------------------------- globals ------------------------------- */
static std::string g_hist_file;
static std::string home_dir() {
const char *h = std::getenv("HOME");
if (h && *h) return h;
return ".";
}
/* ----------------------------- history -------------------------------- */
void load_history() {
g_hist_file = home_dir() + "/.bantam_history";
using_history();
/* A missing history file is not an error. */
read_history(g_hist_file.c_str());
}
void add_history(std::string_view s) {
if (s.empty()) return;
std::string line(s);
/* Skip an entry identical to the immediately preceding one. */
if (history_length > 0) {
HIST_ENTRY *last = history_get(history_base + history_length - 1);
if (last && last->line && line == last->line) return;
}
::add_history(line.c_str());
}
void save_history() {
if (g_hist_file.empty()) return;
write_history(g_hist_file.c_str());
}
/* ----------------------------- read helpers --------------------------- */
std::string read_plain(std::string_view prompt, bool &ok) {
if (!prompt.empty()) fputs(std::string(prompt).c_str(), stdout);
fflush(stdout);
char *line = nullptr; size_t cap = 0; ssize_t n = getline(&line, &cap, stdin);
if (n < 0) { free(line); clearerr(stdin); ok = false; return ""; }
while (n > 0 && (line[n - 1] == '\n' || line[n - 1] == '\r')) line[--n] = '\0';
std::string r(line, (size_t)n);
free(line);
ok = true;
return r;
}
/* ----------------------------- readline editor ------------------------ */
std::string read_line(std::string_view prompt, bool &ok) {
if (!is_terminal(STDIN_FILENO)) return read_plain(prompt, ok);
/* Enter (\r) submits the line; Ctrl+J inserts a literal newline so a
* multi-line prompt can be composed before submitting. */
rl_bind_key('\n', rl_insert);
char *line = readline(prompt.empty() ? "" : std::string(prompt).c_str());
if (!line) { ok = false; return ""; }
std::string r(line);
free(line);
ok = true;
return r;
}
/* ----------------------------- term helpers --------------------------- */
int is_terminal(int fd) { return isatty(fd) != 0; }
int term_width() {
#ifdef TIOCGWINSZ
struct winsize ws{};
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0) return (int)ws.ws_col;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0) return (int)ws.ws_col;
#endif
const char *c = std::getenv("COLUMNS");
if (c && *c) { int n = std::atoi(c); if (n > 0) return n; }
FILE *p = popen("tput cols 2>/dev/null", "r");
if (p) {
char buf[32];
if (fgets(buf, sizeof buf, p)) {
int n = std::atoi(buf);
pclose(p);
if (n > 0) return n;
} else pclose(p);
}
return 80;
}