summaryrefslogtreecommitdiffstats
path: root/src/readline_curses.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/readline_curses.cc492
1 files changed, 383 insertions, 109 deletions
diff --git a/src/readline_curses.cc b/src/readline_curses.cc
index f74a45c..326b453 100644
--- a/src/readline_curses.cc
+++ b/src/readline_curses.cc
@@ -30,9 +30,11 @@
*/
#include <errno.h>
+#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -56,12 +58,13 @@
#include "base/ansi_scrubber.hh"
#include "base/auto_mem.hh"
+#include "base/fs_util.hh"
+#include "base/itertools.hh"
#include "base/lnav_log.hh"
#include "base/paths.hh"
#include "base/string_util.hh"
#include "fmt/format.h"
#include "fts_fuzzy_match.hh"
-#include "lnav_util.hh"
#include "readline_curses.hh"
#include "shlex.hh"
#include "spookyhash/SpookyV2.h"
@@ -89,6 +92,8 @@ static const char* RL_INIT[] = {
"set menu-complete-display-prefix on",
"TAB: menu-complete",
"\"\\e[Z\": menu-complete-backward",
+ "\"\\eC\": lnav-forward-complete",
+ "\"\\C-f\": lnav-forward-complete",
};
readline_context* readline_context::loaded_context;
@@ -97,6 +102,7 @@ static std::string last_match_str;
static bool last_match_str_valid;
static bool arg_needs_shlex;
static nonstd::optional<std::string> rewrite_line_start;
+static std::string rc_local_suggestion;
static void
sigalrm(int sig)
@@ -235,10 +241,10 @@ readline_context::completion_generator(const char* text_in, int state)
if (arg_needs_shlex) {
shlex arg_lexer(text_str);
- std::map<std::string, std::string> scope;
+ std::map<std::string, scoped_value_t> scope;
std::string result;
- if (arg_lexer.eval(result, scope)) {
+ if (arg_lexer.eval(result, scoped_resolver{&scope})) {
text_str = result;
}
}
@@ -364,28 +370,75 @@ readline_context::attempted_completion(const char* text, int start, int end)
{
char** retval = nullptr;
+ log_info("completion start %d:%d -- %s", start, end, text);
+
+ auto at_start = start == 0;
+ auto cmd_start = 0;
+ auto cmd_key = std::string("__command");
+ if (loaded_context->rc_splitter != nullptr) {
+ auto split_res
+ = loaded_context->rc_splitter(*loaded_context, rl_line_buffer);
+
+ readline_context::command_t* last_cmd = nullptr;
+ for (const auto& stage : split_res.sr_stages) {
+ if (stage.s_args.empty()) {
+ continue;
+ }
+
+ if (stage.s_args.front().lr_start == start) {
+ at_start = true;
+ break;
+ }
+ if (start <= stage.s_args.back().lr_end) {
+ cmd_start = stage.s_args.front().lr_start;
+ }
+ auto cmd_lr = stage.s_args.front();
+ auto cmd_name = std::string(&rl_line_buffer[cmd_lr.lr_start],
+ cmd_lr.length());
+ auto cmd_iter = loaded_context->rc_commands.find(cmd_name);
+ if (cmd_iter == loaded_context->rc_commands.end()) {
+ continue;
+ }
+ last_cmd = cmd_iter->second;
+ }
+ if (last_cmd != nullptr && !last_cmd->c_provides.empty()) {
+ cmd_key
+ = fmt::format(FMT_STRING("__command_{}"), last_cmd->c_provides);
+ }
+ }
+
completion_start = start;
- if (start == 0
- && loaded_context->rc_possibilities.find("__command")
- != loaded_context->rc_possibilities.end())
+ if (text[0] == '\0' && !rc_local_suggestion.empty()) {
+ static std::set<std::string> suggestion_possibilities;
+
+ suggestion_possibilities.clear();
+ suggestion_possibilities.emplace(rc_local_suggestion);
+ arg_possibilities = &suggestion_possibilities;
+ rl_completion_append_character = 0;
+ } else if (at_start
+ && loaded_context->rc_possibilities.find(cmd_key)
+ != loaded_context->rc_possibilities.end())
{
- arg_possibilities = &loaded_context->rc_possibilities["__command"];
+ arg_possibilities = &loaded_context->rc_possibilities[cmd_key];
arg_needs_shlex = false;
rl_completion_append_character = loaded_context->rc_append_character;
} else {
char* space;
std::string cmd;
- std::vector<std::string> prefix;
int point = rl_point;
while (point > 0 && rl_line_buffer[point] != ' ') {
point -= 1;
}
shlex lexer(rl_line_buffer, point);
- std::map<std::string, std::string> scope;
+ std::map<std::string, scoped_value_t> scope;
arg_possibilities = nullptr;
rl_completion_append_character = 0;
- if (lexer.split(prefix, scope)) {
+ auto split_res = lexer.split(scoped_resolver{&scope});
+ if (split_res.isOk()) {
+ auto prefix = split_res.unwrap()
+ | lnav::itertools::map(
+ [](const auto& elem) { return elem.se_value; });
auto prefix2
= fmt::format(FMT_STRING("{}"), fmt::join(prefix, "\x1f"));
auto prefix_iter = loaded_context->rc_prefixes.find(prefix2);
@@ -398,12 +451,12 @@ readline_context::attempted_completion(const char* text, int start, int end)
}
if (arg_possibilities == nullptr) {
- space = strchr(rl_line_buffer, ' ');
+ space = strchr(&rl_line_buffer[cmd_start], ' ');
if (space == nullptr) {
space = rl_line_buffer + strlen(rl_line_buffer);
}
- cmd = std::string(rl_line_buffer, space - rl_line_buffer);
-
+ cmd = std::string(&rl_line_buffer[cmd_start],
+ space - &rl_line_buffer[cmd_start]);
auto iter = loaded_context->rc_prototypes.find(cmd);
if (iter == loaded_context->rc_prototypes.end()) {
@@ -416,71 +469,107 @@ readline_context::attempted_completion(const char* text, int start, int end)
= loaded_context->rc_append_character;
}
} else {
- std::vector<std::string>& proto
- = loaded_context->rc_prototypes[cmd];
+ auto& proto = loaded_context->rc_prototypes[cmd];
if (proto.empty()) {
arg_possibilities = nullptr;
- } else if (proto[0] == "filename") {
+ } else if (proto[0] == "dirname") {
shlex fn_lexer(rl_line_buffer, rl_point);
- std::vector<std::string> fn_list;
- int found = 0;
-
- fn_lexer.split(fn_list, scope);
-
- const auto& last_fn = fn_list.size() <= 1 ? ""
- : fn_list.back();
+ auto split_res = fn_lexer.split(scoped_resolver{&scope});
+ if (split_res.isOk()) {
+ auto fn_list = split_res.unwrap();
+ const auto& last_fn = fn_list.size() <= 1
+ ? ""
+ : fn_list.back().se_value;
- if (last_fn.find(':') != std::string::npos) {
- auto rp_iter = loaded_context->rc_possibilities.find(
- "remote-path");
- if (rp_iter != loaded_context->rc_possibilities.end()) {
- for (const auto& poss : rp_iter->second) {
- if (startswith(poss, last_fn.c_str())) {
- found += 1;
- }
- }
- if (found) {
- arg_possibilities = &rp_iter->second;
- arg_needs_shlex = false;
- }
- }
- if (!found || (endswith(last_fn, "/") && found == 1)) {
- char msg[2048];
-
- snprintf(
- msg, sizeof(msg), "\t:%s", last_fn.c_str());
- sendstring(child_this->rc_command_pipe[1],
- msg,
- strlen(msg));
- }
- }
- if (!found) {
- static std::set<std::string> file_name_set;
+ static std::set<std::string> dir_name_set;
- file_name_set.clear();
+ dir_name_set.clear();
auto_mem<char> completed_fn;
int fn_state = 0;
- auto recent_netlocs_iter
- = loaded_context->rc_possibilities.find(
- "recent-netlocs");
- if (recent_netlocs_iter
- != loaded_context->rc_possibilities.end())
- {
- file_name_set.insert(
- recent_netlocs_iter->second.begin(),
- recent_netlocs_iter->second.end());
- }
while ((completed_fn = rl_filename_completion_function(
last_fn.c_str(), fn_state))
!= nullptr)
{
- file_name_set.insert(completed_fn.in());
+ dir_name_set.insert(completed_fn.in());
fn_state += 1;
}
- arg_possibilities = &file_name_set;
+ arg_possibilities = &dir_name_set;
arg_needs_shlex = true;
+ } else {
+ arg_possibilities = nullptr;
+ }
+ } else if (proto[0] == "filename") {
+ shlex fn_lexer(rl_line_buffer, rl_point);
+ int found = 0;
+
+ auto split_res = fn_lexer.split(scoped_resolver{&scope});
+ if (split_res.isOk()) {
+ auto fn_list = split_res.unwrap();
+ const auto& last_fn = fn_list.size() <= 1
+ ? ""
+ : fn_list.back().se_value;
+
+ if (last_fn.find(':') != std::string::npos) {
+ auto rp_iter
+ = loaded_context->rc_possibilities.find(
+ "remote-path");
+ if (rp_iter
+ != loaded_context->rc_possibilities.end())
+ {
+ for (const auto& poss : rp_iter->second) {
+ if (startswith(poss, last_fn.c_str())) {
+ found += 1;
+ }
+ }
+ if (found) {
+ arg_possibilities = &rp_iter->second;
+ arg_needs_shlex = false;
+ }
+ }
+ if (!found
+ || (endswith(last_fn, "/") && found == 1))
+ {
+ char msg[2048];
+
+ snprintf(
+ msg, sizeof(msg), "\t:%s", last_fn.c_str());
+ sendstring(child_this->rc_command_pipe[1],
+ msg,
+ strlen(msg));
+ }
+ }
+ if (!found) {
+ static std::set<std::string> file_name_set;
+
+ file_name_set.clear();
+ auto_mem<char> completed_fn;
+ int fn_state = 0;
+ auto recent_netlocs_iter
+ = loaded_context->rc_possibilities.find(
+ "recent-netlocs");
+
+ if (recent_netlocs_iter
+ != loaded_context->rc_possibilities.end())
+ {
+ file_name_set.insert(
+ recent_netlocs_iter->second.begin(),
+ recent_netlocs_iter->second.end());
+ }
+ while (
+ (completed_fn = rl_filename_completion_function(
+ last_fn.c_str(), fn_state))
+ != nullptr)
+ {
+ file_name_set.insert(completed_fn.in());
+ fn_state += 1;
+ }
+ arg_possibilities = &file_name_set;
+ arg_needs_shlex = true;
+ }
+ } else {
+ arg_possibilities = nullptr;
}
} else {
arg_possibilities
@@ -500,6 +589,22 @@ readline_context::attempted_completion(const char* text, int start, int end)
}
static int
+lnav_forward_complete(int count, int key)
+{
+ if (!rc_local_suggestion.empty() && rl_point == rl_end) {
+ rl_extend_line_buffer(strlen(rl_line_buffer)
+ + rc_local_suggestion.size() + 1);
+ strcat(rl_line_buffer, rc_local_suggestion.c_str());
+ rl_point = strlen(rl_line_buffer);
+ rl_end = rl_point;
+ rc_local_suggestion.clear();
+ return 0;
+ }
+
+ return rl_forward_char(count, key);
+}
+
+static int
rubout_char_or_abort(int count, int key)
{
if (rl_line_buffer[0] == '\0') {
@@ -546,8 +651,24 @@ readline_context::readline_context(std::string name,
for (iter = commands->begin(); iter != commands->end(); ++iter) {
std::string cmd = iter->first;
+ auto cmd_complete = cmd;
+ const auto& ht = iter->second->c_help;
- this->rc_possibilities["__command"].insert(cmd);
+ if (!ht.ht_parameters.empty()
+ && ht.ht_parameters.front().ht_group_start != nullptr)
+ {
+ cmd_complete.append(" ");
+ cmd_complete.append(ht.ht_parameters.front().ht_group_start);
+ }
+ if (iter->second->c_dependencies.empty()) {
+ this->rc_possibilities["__command"].insert(cmd_complete);
+ } else {
+ for (const auto& dep : iter->second->c_dependencies) {
+ auto cmd_key = fmt::format(FMT_STRING("__command_{}"), dep);
+ this->rc_possibilities[cmd_key].insert(cmd_complete);
+ }
+ }
+ this->rc_commands[cmd] = iter->second;
iter->second->c_func(
INIT_EXEC_CONTEXT, cmd, this->rc_prototypes[cmd]);
}
@@ -560,8 +681,12 @@ readline_context::readline_context(std::string name,
auto hpath = (config_dir / this->rc_name).string() + ".history";
read_history(hpath.c_str());
this->save();
+}
- this->rc_append_character = ' ';
+void
+readline_context::set_history()
+{
+ history_set_history_state(&this->rc_history);
}
void
@@ -690,7 +815,9 @@ readline_curses::start()
}
this->rc_command_pipe[RCF_MASTER] = sp[RCF_MASTER];
+ this->rc_command_pipe[RCF_MASTER].close_on_exec();
this->rc_command_pipe[RCF_SLAVE] = sp[RCF_SLAVE];
+ this->rc_command_pipe[RCF_SLAVE].close_on_exec();
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) {
throw error(errno);
@@ -699,21 +826,61 @@ readline_curses::start()
if (this->vc_width > 0) {
ws.ws_col = this->vc_width;
} else if (this->vc_width < 0) {
- ws.ws_col -= this->vc_left;
+ ws.ws_col -= this->vc_x;
ws.ws_col += this->vc_width;
}
- if (openpty(this->rc_pty[RCF_MASTER].out(),
- this->rc_pty[RCF_SLAVE].out(),
- nullptr,
- nullptr,
- &ws)
- < 0)
+
+ auto openpt_res = auto_fd::openpt(O_NOCTTY | O_RDWR);
+ if (openpt_res.isErr()) {
+ log_error("readline_curses: cannot open pty -- %s",
+ openpt_res.unwrapErr().c_str());
+ throw error(errno);
+ }
+
+ this->rc_pty[RCF_MASTER] = openpt_res.unwrap();
+ log_perror(grantpt(this->rc_pty[RCF_MASTER]));
+ log_perror(unlockpt(this->rc_pty[RCF_MASTER]));
+ char slave_path_str[PATH_MAX];
+ if (ptsname_r(
+ this->rc_pty[RCF_MASTER], slave_path_str, sizeof(slave_path_str))
+ == -1)
{
- perror("error: failed to open terminal(openpty)");
+ perror("ptsname_r");
+ throw error(errno);
+ }
+
+ auto slave_path = ghc::filesystem::path(slave_path_str);
+ std::error_code ec;
+ if (!ghc::filesystem::exists(slave_path, ec)) {
+ log_warning("ptsname_r() result does not exist -- %s", slave_path_str);
+#ifdef TIOCGPTN
+ int ptn = 0;
+ if (ioctl(this->rc_pty[RCF_MASTER], TIOCGPTN, &ptn) == 0) {
+ snprintf(
+ slave_path_str, sizeof(slave_path_str), "/dev/ttyp%d", ptn);
+ slave_path = ghc::filesystem::path(slave_path_str);
+ log_warning("... trying %s", slave_path.c_str());
+ }
+#endif
+ }
+ auto slave_open_res = lnav::filesystem::open_file(
+ slave_path, O_RDWR | O_NOCTTY | O_CLOEXEC);
+ if (slave_open_res.isErr()) {
+ log_error("open pseudo failed -- %s",
+ slave_open_res.unwrapErr().c_str());
+ throw error(errno);
+ }
+ this->rc_pty[RCF_SLAVE] = slave_open_res.unwrap();
+
+ if (ioctl(this->rc_pty[RCF_SLAVE], TIOCSWINSZ, &ws) == -1) {
throw error(errno);
}
+ this->rc_pty[RCF_MASTER].close_on_exec();
+ this->rc_pty[RCF_SLAVE].close_on_exec();
+
if ((this->rc_child = fork()) == -1) {
+ log_error("fork() failed -- %s", strerror(errno));
throw error(errno);
}
@@ -731,7 +898,7 @@ readline_curses::start()
signal(SIGALRM, sigalrm);
signal(SIGWINCH, sigwinch);
- signal(SIGINT, sigterm);
+ signal(SIGINT, SIG_IGN);
signal(SIGTERM, sigterm);
dup2(this->rc_pty[RCF_SLAVE], STDIN_FILENO);
@@ -743,6 +910,7 @@ readline_curses::start()
using_history();
stifle_history(HISTORY_SIZE);
+ rl_add_defun("lnav-forward-complete", lnav_forward_complete, -1);
rl_add_defun("rubout-char-or-abort", rubout_char_or_abort, '\b');
rl_add_defun("alt-done", alt_done_func, '\x0a');
// rl_add_defun("command-complete", readline_context::command_complete,
@@ -750,7 +918,10 @@ readline_curses::start()
for (const auto* init_cmd : RL_INIT) {
snprintf(buffer, sizeof(buffer), "%s", init_cmd);
- rl_parse_and_bind(buffer); /* NOTE: buffer is modified */
+ /* NOTE: buffer is modified */
+ if (rl_parse_and_bind(buffer)) {
+ log_error("rl_parse_and_bind(%s) failed", init_cmd);
+ }
}
child_this = this;
@@ -767,6 +938,8 @@ readline_curses::start()
maxfd = std::max(STDIN_FILENO, this->rc_command_pipe[RCF_SLAVE].get());
+ static uint64_t last_h1, last_h2;
+
while (looping) {
fd_set ready_rfds;
int rc;
@@ -785,8 +958,6 @@ readline_curses::start()
}
} else {
if (FD_ISSET(STDIN_FILENO, &ready_rfds)) {
- static uint64_t last_h1, last_h2;
-
struct itimerval itv;
itv.it_value.tv_sec = 0;
@@ -794,7 +965,6 @@ readline_curses::start()
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &itv, nullptr);
-
rl_callback_read_char();
if (RL_ISSTATE(RL_STATE_DONE) && !got_line) {
got_line = 1;
@@ -842,14 +1012,17 @@ readline_curses::start()
if (h1 == last_h1 && h2 == last_h2) {
// do nothing
- } else if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
- complete_done ? 'l' : 'c',
- rl_line_buffer,
- rl_end)
- != 0)
- {
- perror("line: write failed");
- _exit(1);
+ } else {
+ rc_local_suggestion.clear();
+ if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
+ complete_done ? 'l' : 'c',
+ rl_line_buffer,
+ rl_end)
+ != 0)
+ {
+ perror("line: write failed");
+ _exit(1);
+ }
}
last_h1 = h1;
last_h2 = h2;
@@ -862,7 +1035,7 @@ readline_curses::start()
}
}
if (FD_ISSET(this->rc_command_pipe[RCF_SLAVE], &ready_rfds)) {
- char msg[1024 + 1];
+ char msg[8 + MAXPATHLEN + 1024];
if ((rc = recvstring(this->rc_command_pipe[RCF_SLAVE],
msg,
@@ -871,11 +1044,31 @@ readline_curses::start()
{
looping = false;
} else {
- int context, prompt_start = 0;
+ int context, prompt_start = 0, new_point = 0;
char type[1024];
msg[rc] = '\0';
- if (sscanf(msg, "i:%d:%n", &rl_point, &prompt_start) == 1) {
+ if (startswith(msg, "cd:")) {
+ const char* cwd = &msg[3];
+
+ log_perror(chdir(cwd));
+ } else if (startswith(msg, "sugg:")) {
+ rc_local_suggestion = &msg[5];
+ } else if (sscanf(msg, "x:%d", &new_point) == 1) {
+ if (rl_prompt) {
+ new_point -= strlen(rl_prompt);
+ }
+ if (new_point < 0) {
+ new_point = 0;
+ }
+ if (new_point > rl_end) {
+ new_point = rl_end;
+ }
+ rl_point = new_point;
+ rl_redisplay();
+ } else if (sscanf(msg, "i:%d:%n", &rl_point, &prompt_start)
+ == 1)
+ {
const char* initial = &msg[prompt_start];
rl_extend_line_buffer(strlen(initial) + 1);
@@ -978,6 +1171,11 @@ readline_curses::start()
this->rc_contexts[context]->rem_possibility(
std::string(type), std::string(&msg[prompt_start]));
+ } else if (sscanf(msg, "ah:%d:%n", &context, &prompt_start)
+ == 1)
+ {
+ this->rc_contexts[context]->set_history();
+ add_history(&msg[prompt_start]);
} else if (sscanf(msg, "cpre:%d", &context) == 1) {
this->rc_contexts[context]->rc_prefixes.clear();
} else if (sscanf(msg, "cp:%d:%s", &context, type)) {
@@ -1020,6 +1218,7 @@ readline_curses::start()
struct winsize new_ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &new_ws) == -1) {
+ log_error("ioctl() failed -- %s", strerror(errno));
throw error(errno);
}
got_winch = 0;
@@ -1110,6 +1309,23 @@ readline_curses::line_ready(const char* line)
}
void
+readline_curses::append_to_history(int context, const std::string& line)
+{
+ if (line.empty()) {
+ return;
+ }
+
+ char buffer[2048];
+ snprintf(buffer, sizeof(buffer), "ah:%d:%s", context, line.c_str());
+ if (sendstring(
+ this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
+ == -1)
+ {
+ perror("add_possibility: write failed");
+ }
+}
+
+void
readline_curses::check_poll_set(const std::vector<struct pollfd>& pollfds)
{
int rc;
@@ -1119,10 +1335,10 @@ readline_curses::check_poll_set(const std::vector<struct pollfd>& pollfds)
rc = read(this->rc_pty[RCF_MASTER], buffer, sizeof(buffer));
if (rc > 0) {
- int old_x = this->vc_x;
+ int old_x = this->vc_cursor_x;
this->map_output(buffer, rc);
- if (this->vc_x != old_x) {
+ if (this->vc_cursor_x != old_x) {
this->rc_change(this);
}
}
@@ -1205,9 +1421,10 @@ readline_curses::check_poll_set(const std::vector<struct pollfd>& pollfds)
this->rc_blur(this);
break;
- case 'l':
+ case 'l': {
this->rc_line_buffer = &msg[2];
if (this->rc_active_context != -1) {
+ this->rc_suggestion.clear();
this->rc_change(this);
}
this->rc_matches.clear();
@@ -1215,9 +1432,11 @@ readline_curses::check_poll_set(const std::vector<struct pollfd>& pollfds)
this->rc_display_match(this);
}
break;
+ }
case 'c':
this->rc_line_buffer = &msg[2];
+ this->rc_suggestion.clear();
this->rc_change(this);
this->rc_display_match(this);
break;
@@ -1247,12 +1466,21 @@ readline_curses::focus(int context,
const std::string& prompt,
const std::string& initial)
{
- char buffer[1024];
+ char cwd[MAXPATHLEN + 1024];
+ char buffer[8 + sizeof(cwd)];
curs_set(1);
this->rc_active_context = context;
+ getcwd(cwd, sizeof(cwd));
+ snprintf(buffer, sizeof(buffer), "cd:%s", cwd);
+ if (sendstring(
+ this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
+ == -1)
+ {
+ perror("focus: write failed");
+ }
snprintf(buffer, sizeof(buffer), "f:%d:%s", context, prompt.c_str());
if (sendstring(
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
@@ -1260,8 +1488,13 @@ readline_curses::focus(int context,
{
perror("focus: write failed");
}
- wmove(this->vc_window, this->get_actual_y(), this->vc_left);
- wclrtoeol(this->vc_window);
+ auto al = attr_line_t();
+ al.append(lnav::roles::suggestion(this->rc_suggestion));
+ view_curses::mvwattrline(this->vc_window,
+ this->get_actual_y(),
+ this->vc_x,
+ al,
+ line_range{0, (int) this->get_actual_width()});
if (!initial.empty()) {
this->rewrite_line(initial.size(), initial);
}
@@ -1284,11 +1517,27 @@ readline_curses::rewrite_line(int pos, const std::string& value)
}
void
+readline_curses::set_suggestion(const std::string& value)
+{
+ char buffer[1024];
+
+ snprintf(buffer, sizeof(buffer), "sugg:%s", value.c_str());
+ if (sendstring(
+ this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
+ == -1)
+ {
+ perror("set_suggestion: write failed");
+ }
+ this->rc_suggestion = value;
+ this->set_needs_update();
+}
+
+void
readline_curses::abort()
{
char buffer[1024];
- this->vc_x = 0;
+ this->vc_cursor_x = 0;
snprintf(buffer, sizeof(buffer), "a");
if (sendstring(this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer))
== -1)
@@ -1393,11 +1642,11 @@ readline_curses::clear_possibilities(int context, std::string type)
}
}
-void
+bool
readline_curses::do_update()
{
if (!this->vc_visible || this->vc_window == nullptr) {
- return;
+ return false;
}
auto actual_width = this->get_actual_width();
@@ -1407,7 +1656,7 @@ readline_curses::do_update()
attr_line_t alt_al;
auto& vc = view_colors::singleton();
- wmove(this->vc_window, this->get_actual_y(), this->vc_left);
+ wmove(this->vc_window, this->get_actual_y(), this->vc_x);
auto attrs = vc.attrs_for_role(role_t::VCR_TEXT);
wattr_set(this->vc_window,
attrs.ta_attrs,
@@ -1435,35 +1684,58 @@ readline_curses::do_update()
lr.lr_end = this->rc_value.length();
view_curses::mvwattrline(this->vc_window,
this->get_actual_y(),
- this->vc_left,
+ this->vc_x,
this->rc_value,
lr);
- this->set_x(0);
+ this->set_cursor_x(0);
}
if (this->rc_active_context != -1) {
- readline_context* rc = this->rc_contexts[this->rc_active_context];
- readline_highlighter_t hl = rc->get_highlighter();
- attr_line_t al = this->vc_line;
+ auto* rc = this->rc_contexts[this->rc_active_context];
+ auto hl = rc->get_highlighter();
+ auto al = this->vc_line;
if (hl != nullptr) {
- hl(al, this->vc_left + this->vc_x);
+ hl(al, this->vc_x + this->vc_cursor_x);
}
+ al.append(lnav::roles::suggestion(this->rc_suggestion));
view_curses::mvwattrline(this->vc_window,
this->get_actual_y(),
- this->vc_left,
+ this->vc_x,
al,
line_range{0, (int) actual_width});
- wmove(
- this->vc_window, this->get_actual_y(), this->vc_left + this->vc_x);
+ wmove(this->vc_window,
+ this->get_actual_y(),
+ this->vc_x + this->vc_cursor_x);
+ }
+
+ return true;
+}
+
+bool
+readline_curses::handle_mouse(mouse_event& me)
+{
+ if (this->rc_active_context == -1) {
+ return false;
+ }
+
+ char buffer[32];
+
+ snprintf(buffer, sizeof(buffer), "x:%d", me.me_x);
+ if (sendstring(
+ this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
+ == -1)
+ {
+ perror("handle_mouse: write failed");
}
+ return true;
}
std::string
readline_curses::get_match_string() const
{
- auto len = std::min((size_t) this->vc_x, this->rc_line_buffer.size())
+ auto len = std::min((size_t) this->vc_cursor_x, this->rc_line_buffer.size())
- this->rc_match_start;
auto* context = this->get_active_context();
@@ -1520,15 +1792,17 @@ readline_curses::window_change()
struct winsize ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) {
+ log_error("ioctl() failed -- %s", strerror(errno));
throw error(errno);
}
if (this->vc_width > 0) {
ws.ws_col = this->vc_width;
} else if (this->vc_width < 0) {
- ws.ws_col -= this->vc_left;
+ ws.ws_col -= this->vc_x;
ws.ws_col += this->vc_width;
}
if (ioctl(this->rc_pty[RCF_MASTER], TIOCSWINSZ, &ws) == -1) {
+ log_error("ioctl() failed -- %s", strerror(errno));
throw error(errno);
}
kill(this->rc_child, SIGWINCH);