From eb37b0e351578952156b5d5a6f32c701e6bd06ce Mon Sep 17 00:00:00 2001 From: Chris Antos Date: Sun, 10 Nov 2024 02:29:59 -0800 Subject: [PATCH] Move all RPROMPT code from readline into Clink. This cleans out the Readline modifications for RPROMPT support. I abandoned the goal of contributing RPROMPT support back into Readline because its display code has limitations that created troublesome edge cases. And since Clink abandoned Readline's display code altogether, I don't have a good way to test whether the RPROMPT implementation would work properly in bash. --- TODO.md | 1 - clink/lib/include/lib/display_readline.h | 3 + clink/lib/src/display_readline.cpp | 65 +++++++++++++- clink/lua/src/rl_api.cpp | 4 +- readline/readline/display.c | 105 ++++------------------- readline/readline/readline.c | 6 -- readline/readline/readline.h | 8 -- 7 files changed, 82 insertions(+), 110 deletions(-) diff --git a/TODO.md b/TODO.md index bd7f12e14..f2c0c6068 100644 --- a/TODO.md +++ b/TODO.md @@ -12,7 +12,6 @@ _This todo list describes ChrisAnt996's current intended roadmap for Clink's fut ## Normal Priority - Randomly hit `assert(group == m_prev_group || group == m_catch_group);` upon `Ctrl-Space`. It left input in a weird state with `clink-select-complete` still active but not handling input. Could not repro again after I got out of the state. It seems likely to be a long-standing issue in some obscure edge case. -- Finish removing the RPROMPT stuff from Readline, moving it entirely into Clink code. The only part of Readline that actually needs it is the stuff for clearing to the end of the line, which is mostly omitted now anyway. Most of it can be moved easily, but one spot will need special consideration (maybe a callback?): `_rl_erase_entire_line()` inside `_rl_internal_char_cleanup()`. - Event handler enhancements: - Allow setting an optional `priority` when registering event handlers? So that scripts can control the precedence of `onbeginedit`, `onendedit`, and so on. - Allow adding a ONE-TIME event handler which automatically removes itself upon firing? And `clink-diagnostics` would need to show any ONE-TIME event handlers until the next beginedit. diff --git a/clink/lib/include/lib/display_readline.h b/clink/lib/include/lib/display_readline.h index 659277ad8..a1e2bf3c6 100644 --- a/clink/lib/include/lib/display_readline.h +++ b/clink/lib/include/lib/display_readline.h @@ -14,6 +14,9 @@ class line_buffer; typedef struct _history_expansion history_expansion; +extern char* rl_rprompt; +void rl_set_rprompt(const char* rprompt); + extern "C" void reset_display_readline(void); void refresh_terminal_size(); void clear_to_end_of_screen_on_next_display(); diff --git a/clink/lib/src/display_readline.cpp b/clink/lib/src/display_readline.cpp index edd146e32..d3e463992 100644 --- a/clink/lib/src/display_readline.cpp +++ b/clink/lib/src/display_readline.cpp @@ -75,10 +75,14 @@ extern int rl_get_forced_display(void); extern void rl_set_forced_display(int force); extern int _rl_last_v_pos; -extern int _rl_rprompt_shown_len; - +int _rl_rprompt_shown_len = 0; } // extern "C" +// rl_prompt is the right-justified prompt string, if any. It is set by +// rl_set_rprompt(), and should not be assigned to directly. +char* rl_rprompt = nullptr; +int32 rl_visible_rprompt_length = 0; + #ifndef HANDLE_MULTIBYTE #error HANDLE_MULTIBYTE is required. #endif @@ -2771,6 +2775,63 @@ extern "C" void _rl_refresh_line(void) rl_keep_mark_active(); } +//------------------------------------------------------------------------------ +extern "C" void _rl_clear_to_eol(int32 count) +{ + if (_rl_last_v_pos == 0) + { + // If the cursor is on the first line of the input buffer, then flag + // that the right side prompt is not shown, so it can be redisplayed + // later as appropriate. + _rl_rprompt_shown_len = 0; + } + + assert(_rl_term_clreol); + assert(*_rl_term_clreol); + tputs(_rl_term_clreol); +} + +//------------------------------------------------------------------------------ +static char* expand_rprompt(const char* pmt) +{ + const uint32 l = str_len(pmt); + char* ret = (char*)xmalloc(l + 1); + bool newlines = false; + + // Strip the invisible character string markers RL_PROMPT_START_IGNORE and + // RL_PROMPT_END_IGNORE. + char* r = ret; + for (const char* p = pmt; *p; ++p) + { + if (*p == '\r' || *p == '\n') + newlines = true; + if (*p != RL_PROMPT_START_IGNORE && *p != RL_PROMPT_END_IGNORE) + *(r++) = *p; + } + *r = '\0'; + + if (newlines) + { + free(ret); + ret = nullptr; + } + + return ret; +} + +//------------------------------------------------------------------------------ +void rl_set_rprompt(const char* rprompt) +{ + free(rl_rprompt); + + if (rprompt && *rprompt) + rl_rprompt = expand_rprompt(rprompt); + else + rl_rprompt = nullptr; + + rl_visible_rprompt_length = rl_rprompt ? cell_count(rl_rprompt) : 0; +} + //------------------------------------------------------------------------------ void refresh_terminal_size() { diff --git a/clink/lua/src/rl_api.cpp b/clink/lua/src/rl_api.cpp index 031de6fb6..7e29d3ae0 100644 --- a/clink/lua/src/rl_api.cpp +++ b/clink/lua/src/rl_api.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "match_builder_lua.h" #include "prompt.h" @@ -30,9 +31,6 @@ extern "C" { #include #include #include -extern int _rl_completion_case_map; -extern const char* rl_readline_name; -extern int _rl_last_v_pos; } // TODO: Clean up extern. diff --git a/readline/readline/display.c b/readline/readline/display.c index e62468fd6..eb20d71e5 100644 --- a/readline/readline/display.c +++ b/readline/readline/display.c @@ -76,9 +76,7 @@ static void norm_face (char *, int); #if !defined (OMIT_DEFAULT_DISPLAY_READLINE) static void update_line (char *, char *, char *, char *, int, int, int, int); -#endif /* !OMIT_DEFAULT_DISPLAY_READLINE */ static void space_to_eol (int); -#if !defined (OMIT_DEFAULT_DISPLAY_READLINE) static void delete_chars (int); static void insert_some_chars (char *, int, int); static void open_some_spaces (int); @@ -89,7 +87,6 @@ static void _rl_move_cursor_relative (int, const char *, const char *); /* Values for FLAGS */ #define PMT_MULTILINE 0x01 -#define PMT_RPROMPT 0x02 /* begin_clink_change */ //static char *expand_prompt (char *, int, int *, int *, int *, int *); @@ -220,13 +217,6 @@ int rl_display_fixed = 0; This is usually pointing to rl_prompt. */ char *rl_display_prompt = (char *)NULL; -/* begin_clink_change */ -/* The right side prompt, if any. This is displayed on the first - line of the input text, if there is room past the input text. */ -char *rl_display_rprompt = (char *)NULL; -int _rl_rprompt_shown_len = 0; -/* end_clink_change */ - /* Variables used to include the editing mode in the prompt. */ char *_rl_emacs_mode_str; int _rl_emacs_modestr_len; @@ -402,7 +392,6 @@ prompt_modestr (int *lenp) /* Possible values for FLAGS: PMT_MULTILINE caller indicates that this is part of a multiline prompt - PMT_RPROMPT caller indicates that this is the right side prompt */ /* This approximates the number of lines the prompt will take when displayed */ @@ -466,15 +455,10 @@ expand_prompt (const char *pmt, int flags, int *lp, int *lip, int *niflp, int *v if (vlp) *vlp = l; -/* begin_clink_change */ - if (!(flags & PMT_RPROMPT)) -/* end_clink_change */ - { - local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * 2); - local_prompt_invis_chars = (int *) xrealloc (local_prompt_invis_chars, sizeof (int) * 2); - local_prompt_newlines[0] = local_prompt_invis_chars[0] = 0; - local_prompt_newlines[1] = -1; - } + local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * 2); + local_prompt_invis_chars = (int *) xrealloc (local_prompt_invis_chars, sizeof (int) * 2); + local_prompt_newlines[0] = local_prompt_invis_chars[0] = 0; + local_prompt_newlines[1] = -1; return r; } @@ -486,19 +470,14 @@ expand_prompt (const char *pmt, int flags, int *lp, int *lip, int *niflp, int *v /* Guess at how many screen lines the prompt will take to size the array that keeps track of where the line wraps happen */ newlines_guess = (_rl_screenwidth > 0) ? APPROX_DIV(l, _rl_screenwidth) : APPROX_DIV(l, 80); -/* begin_clink_change */ - if (!(flags & PMT_RPROMPT)) -/* end_clink_change */ + local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * (newlines_guess + 1)); + local_prompt_newlines[newlines = 0] = 0; + local_prompt_invis_chars = (int *) xrealloc (local_prompt_invis_chars, sizeof (int) * (newlines_guess + 1)); + local_prompt_invis_chars[0] = 0; + for (rl = 1; rl <= newlines_guess; rl++) { - local_prompt_newlines = (int *) xrealloc (local_prompt_newlines, sizeof (int) * (newlines_guess + 1)); - local_prompt_newlines[newlines = 0] = 0; - local_prompt_invis_chars = (int *) xrealloc (local_prompt_invis_chars, sizeof (int) * (newlines_guess + 1)); - local_prompt_invis_chars[0] = 0; - for (rl = 1; rl <= newlines_guess; rl++) - { - local_prompt_newlines[rl] = -1; - local_prompt_invis_chars[rl] = 0; - } + local_prompt_newlines[rl] = -1; + local_prompt_invis_chars[rl] = 0; } rl = physchars = 0; /* mode string now part of nprompt */ @@ -594,12 +573,7 @@ expand_prompt (const char *pmt, int flags, int *lp, int *lip, int *niflp, int *v invflset = 1; } -/* begin_clink_change */ - //if (physchars >= (bound = (newlines + 1) * _rl_screenwidth) && local_prompt_newlines[newlines+1] == -1) - if (!(flags & PMT_RPROMPT) && - physchars >= (bound = (newlines + 1) * _rl_screenwidth) && - local_prompt_newlines[newlines+1] == -1) -/* end_clink_change */ + if (physchars >= (bound = (newlines + 1) * _rl_screenwidth) && local_prompt_newlines[newlines+1] == -1) { int new; if (physchars > bound) /* should rarely happen */ @@ -625,11 +599,8 @@ expand_prompt (const char *pmt, int flags, int *lp, int *lip, int *niflp, int *v code that wraps before the physical screen width if the character width would exceed it, but it needs to be checked against this code and local_prompt_newlines[]. */ -/* begin_clink_change */ - //if (ignoring == 0) - if (!(flags & PMT_RPROMPT) && ignoring == 0) -/* end_clink_change */ - can_add_invis = (physchars == bound); + if (ignoring == 0) + can_add_invis = (physchars == bound); } } @@ -652,23 +623,6 @@ expand_prompt (const char *pmt, int flags, int *lp, int *lip, int *niflp, int *v if (nprompt != pmt) xfree (nprompt); -/* begin_clink_change */ - /* Right side prompt is restricted to one line. */ - if ((flags & PMT_RPROMPT) && newlines > 0) - { - xfree (ret); - ret = 0; - if (lp) - *lp = 0; - if (lip) - *lip = 0; - if (niflp) - *niflp = 0; - if (vlp) - *vlp = 0; - } -/* end_clink_change */ - return ret; } @@ -689,28 +643,6 @@ _rl_reset_prompt (void) rl_visible_prompt_length = rl_expand_prompt (rl_prompt); } -/* begin_clink_change */ -/* Set up the right side prompt. Call this before calling readline () - or rl_callback_handler_install (). The string should include - RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE around invisible - characters (escape codes). */ -int -rl_set_rprompt (const char *rprompt) -{ - FREE (rl_rprompt); - - if (rprompt && *rprompt) - { - rl_rprompt = expand_prompt (rprompt, PMT_RPROMPT, (int *)NULL, (int *)NULL, (int *)NULL, &rl_visible_rprompt_length); - } - else - { - rl_rprompt = (char *)NULL; - rl_visible_rprompt_length = 0; - } - - return 0; -} /* * Expand the prompt string into the various display components, if * necessary. @@ -3558,7 +3490,6 @@ _rl_erase_at_end_of_line (int l) visible_line[--_rl_last_c_pos] = '\0'; rl_display_fixed++; } -#endif /* OMIT_DEFAULT_DISPLAY_READLINE */ /* Clear to the end of the line. COUNT is the minimum number of character spaces to clear, but we use a terminal escape @@ -3566,13 +3497,6 @@ _rl_erase_at_end_of_line (int l) void _rl_clear_to_eol (int count) { -/* begin_clink_change */ - /* Flag that the right side prompt is not shown, so it can be - redisplayed as appropriate. */ - if (_rl_last_v_pos == 0) - _rl_rprompt_shown_len = 0; -/* end_clink_change */ - #ifndef __MSDOS__ if (_rl_term_clreol) tputs (_rl_term_clreol, 1, _rl_output_character_function); @@ -3594,6 +3518,7 @@ space_to_eol (int count) _rl_last_c_pos += count; } +#endif /* OMIT_DEFAULT_DISPLAY_READLINE */ void _rl_clear_screen (int clrscr) diff --git a/readline/readline/readline.c b/readline/readline/readline.c index cb7d0c444..ddf43a5ae 100644 --- a/readline/readline/readline.c +++ b/readline/readline/readline.c @@ -197,12 +197,6 @@ int _rl_echoing_p = 0; char *rl_prompt = (char *)NULL; int rl_visible_prompt_length = 0; -/* begin_clink_change */ -/* Optional right-justified prompt string. */ -char *rl_rprompt = (char *)NULL; -int rl_visible_rprompt_length = 0; -/* end_clink_change */ - /* Set to non-zero by calling application if it has already printed rl_prompt and does not want readline to do it the first time. */ int rl_already_prompted = 0; diff --git a/readline/readline/readline.h b/readline/readline/readline.h index 544f4d075..169e7f110 100644 --- a/readline/readline/readline.h +++ b/readline/readline/readline.h @@ -333,7 +333,6 @@ extern int rl_expand_prompt (char *); extern const char *rl_get_local_prompt (void); extern const char *rl_get_local_prompt_prefix (void); extern const char *rl_get_message_buffer (void); -extern int rl_set_rprompt (const char *); /* end_clink_change */ extern int rl_initialize (void); @@ -615,13 +614,6 @@ extern char *rl_prompt; applications can more easily supply their own redisplay functions. */ extern char *rl_display_prompt; -/* begin_clink_change */ -/* The right-justified prompt string, if any. This is set by - rl_set_rprompt (), and should not be assigned to directly. */ -extern char *rl_rprompt; -extern int rl_visible_rprompt_length; -/* end_clink_change */ - /* The line buffer that is in use. */ extern char *rl_line_buffer;