From 06ef6842e8ff6b7a754c54f4af39775bc842612b Mon Sep 17 00:00:00 2001 From: MarcoPolo-PasTonMolo Date: Mon, 21 Mar 2022 00:10:07 +0200 Subject: [PATCH 1/2] Add quote autocompletion for previous messages Fixes https://github.com/profanity-im/profanity/issues/1649 Type `>` then press tab or shift tab to autocomplete previous messages, then type your reply and send message. Newlines are replaced with newline followed by `> `. A newline is added at the end so that the user can immediately type a message without manually adding a new line. --- src/ui/chatwin.c | 4 ++++ src/ui/inputwin.c | 27 ++++++++++++++++++++++++++- src/ui/mucwin.c | 3 +++ src/ui/privwin.c | 3 +++ src/ui/win_types.h | 1 + src/ui/window.c | 23 +++++++++++++++++++++++ src/ui/window.h | 2 ++ src/ui/window_list.c | 22 ++++++++++++++++++++++ src/ui/window_list.h | 2 ++ tests/unittests/test_cmd_otr.c | 7 +++++++ 10 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c index 0224d720..d02e4162 100644 --- a/src/ui/chatwin.c +++ b/src/ui/chatwin.c @@ -323,6 +323,7 @@ chatwin_incoming_msg(ProfChatWin* chatwin, ProfMessage* message, gboolean win_cr } wins_add_urls_ac(window, message); + wins_add_quotes_ac(window, message->plain); if (prefs_get_boolean(PREF_BEEP)) { beep(); @@ -346,6 +347,9 @@ chatwin_outgoing_msg(ProfChatWin* chatwin, const char* const message, char* id, { assert(chatwin != NULL); + ProfWin* window = (ProfWin*)chatwin; + wins_add_quotes_ac(window, message); + char* enc_char; if (chatwin->outgoing_char) { enc_char = chatwin->outgoing_char; diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c index 9e34c0b6..d19d8719 100644 --- a/src/ui/inputwin.c +++ b/src/ui/inputwin.c @@ -558,6 +558,10 @@ _inp_rl_getc(FILE* stream) if (_inp_edited(ch)) { ProfWin* window = wins_get_current(); cmd_ac_reset(window); + + if ((window->type == WIN_CHAT || window->type == WIN_MUC || window->type == WIN_PRIVATE) && window->quotes_ac != NULL) { + autocomplete_reset(window->quotes_ac); + } } return ch; } @@ -597,6 +601,17 @@ _inp_rl_tab_handler(int count, int key) } } + if (strncmp(rl_line_buffer, ">", 1) == 0) { + ProfWin* window = wins_get_current(); + char* result = win_quote_autocomplete(window, rl_line_buffer, FALSE); + if (result) { + rl_replace_line(result, 1); + rl_point = rl_end; + free(result); + return 0; + } + } + ProfWin* current = wins_get_current(); if (current->type == WIN_MUC) { char* result = muc_autocomplete(current, rl_line_buffer, FALSE); @@ -607,7 +622,6 @@ _inp_rl_tab_handler(int count, int key) } } - return 0; } @@ -629,6 +643,17 @@ _inp_rl_shift_tab_handler(int count, int key) } } + if (strncmp(rl_line_buffer, ">", 1) == 0) { + ProfWin* window = wins_get_current(); + char* result = win_quote_autocomplete(window, rl_line_buffer, TRUE); + if (result) { + rl_replace_line(result, 1); + rl_point = rl_end; + free(result); + return 0; + } + } + ProfWin* current = wins_get_current(); if (current->type == WIN_MUC) { char* result = muc_autocomplete(current, rl_line_buffer, TRUE); diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c index 5bf21722..c5aed583 100644 --- a/src/ui/mucwin.c +++ b/src/ui/mucwin.c @@ -541,6 +541,8 @@ mucwin_outgoing_msg(ProfMucWin* mucwin, const char* const message, const char* c if (id) { _mucwin_set_last_message(mucwin, id, message); } + + wins_add_quotes_ac(window, message); } void @@ -576,6 +578,7 @@ mucwin_incoming_msg(ProfMucWin* mucwin, const ProfMessage* const message, GSList win_insert_last_read_position_marker((ProfWin*)mucwin, mucwin->roomjid); wins_add_urls_ac(window, message); + wins_add_quotes_ac(window, message->plain); if (g_slist_length(mentions) > 0) { _mucwin_print_mention(window, message->plain, message->from_jid->resourcepart, mynick, mentions, ch, flags); diff --git a/src/ui/privwin.c b/src/ui/privwin.c index 73ef61a0..20611ea4 100644 --- a/src/ui/privwin.c +++ b/src/ui/privwin.c @@ -82,6 +82,7 @@ privwin_incoming_msg(ProfPrivateWin* privatewin, ProfMessage* message) } wins_add_urls_ac(window, message); + wins_add_quotes_ac(window, message->plain); if (prefs_get_boolean(PREF_BEEP)) { beep(); @@ -99,6 +100,8 @@ privwin_outgoing_msg(ProfPrivateWin* privwin, const char* const message) { assert(privwin != NULL); + ProfWin* window = (ProfWin*)privwin; + wins_add_quotes_ac(window, message); win_print_outgoing((ProfWin*)privwin, "-", NULL, NULL, message); } diff --git a/src/ui/win_types.h b/src/ui/win_types.h index f2237a79..70d226b0 100644 --- a/src/ui/win_types.h +++ b/src/ui/win_types.h @@ -148,6 +148,7 @@ typedef struct prof_win_t win_type_t type; ProfLayout* layout; Autocomplete urls_ac; + Autocomplete quotes_ac; } ProfWin; typedef struct prof_console_win_t diff --git a/src/ui/window.c b/src/ui/window.c index 54932f21..516e2209 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -2020,3 +2020,26 @@ win_insert_last_read_position_marker(ProfWin* window, char* id) g_date_time_unref(time); } + +char* +win_quote_autocomplete(ProfWin* window, const char* const input, gboolean previous) +{ + if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE) { + return NULL; + } + + char* result = autocomplete_complete(window->quotes_ac, input + 1, FALSE, previous); + if (result == NULL) { + return NULL; + } + + GString* replace_with = g_string_new("> "); + g_string_append(replace_with, result); + g_string_replace(replace_with, "\n", "\n> ", 0); + g_string_append(replace_with, "\n"); + + g_free(result); + result = replace_with->str; + g_string_free(replace_with, FALSE); + return result; +} diff --git a/src/ui/window.h b/src/ui/window.h index 22a1db51..c7c2853e 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -93,4 +93,6 @@ void win_sub_page_up(ProfWin* window); void win_insert_last_read_position_marker(ProfWin* window, char* id); void win_remove_entry_message(ProfWin* window, const char* const id); +char* win_quote_autocomplete(ProfWin* window, const char* const input, gboolean previous); + #endif diff --git a/src/ui/window_list.c b/src/ui/window_list.c index 01379804..a6ebb5fe 100644 --- a/src/ui/window_list.c +++ b/src/ui/window_list.c @@ -554,6 +554,7 @@ wins_close_by_num(int i) } } autocomplete_free(window->urls_ac); + autocomplete_free(window->quotes_ac); break; } case WIN_MUC: @@ -566,6 +567,7 @@ wins_close_by_num(int i) g_date_time_unref(mucwin->last_msg_timestamp); } autocomplete_free(window->urls_ac); + autocomplete_free(window->quotes_ac); break; } case WIN_PRIVATE: @@ -574,6 +576,7 @@ wins_close_by_num(int i) autocomplete_remove(wins_ac, privwin->fulljid); autocomplete_remove(wins_close_ac, privwin->fulljid); autocomplete_free(window->urls_ac); + autocomplete_free(window->quotes_ac); break; } case WIN_XML: @@ -646,6 +649,7 @@ wins_new_chat(const char* const barejid) } } newwin->urls_ac = autocomplete_new(); + newwin->quotes_ac = autocomplete_new(); return newwin; } @@ -661,6 +665,7 @@ wins_new_muc(const char* const roomjid) autocomplete_add(wins_ac, roomjid); autocomplete_add(wins_close_ac, roomjid); newwin->urls_ac = autocomplete_new(); + newwin->quotes_ac = autocomplete_new(); return newwin; } @@ -688,6 +693,7 @@ wins_new_private(const char* const fulljid) autocomplete_add(wins_ac, fulljid); autocomplete_add(wins_close_ac, fulljid); newwin->urls_ac = autocomplete_new(); + newwin->quotes_ac = autocomplete_new(); return newwin; } @@ -1299,6 +1305,14 @@ wins_add_urls_ac(const ProfWin* const win, const ProfMessage* const message) g_regex_unref(regex); } +void +wins_add_quotes_ac(const ProfWin* const win, const char* const message) +{ + autocomplete_add_reverse(win->quotes_ac, message); + // for people who run profanity a long time, we don't want to waste a lot of memory + autocomplete_remove_older_than_max_reverse(win->quotes_ac, 20); +} + char* wins_get_url(const char* const search_str, gboolean previous, void* context) { @@ -1306,3 +1320,11 @@ wins_get_url(const char* const search_str, gboolean previous, void* context) return autocomplete_complete(win->urls_ac, search_str, FALSE, previous); } + +char* +wins_get_quote(const char* const search_str, gboolean previous, void* context) +{ + ProfWin* win = (ProfWin*)context; + + return autocomplete_complete(win->quotes_ac, search_str, FALSE, previous); +} diff --git a/src/ui/window_list.h b/src/ui/window_list.h index 06ae6d7e..bcb18d14 100644 --- a/src/ui/window_list.h +++ b/src/ui/window_list.h @@ -102,5 +102,7 @@ void win_close_reset_search_attempts(void); void wins_add_urls_ac(const ProfWin* const win, const ProfMessage* const message); char* wins_get_url(const char* const search_str, gboolean previous, void* context); +void wins_add_quotes_ac(const ProfWin* const win, const char* const message); +char* wins_get_quote(const char* const search_str, gboolean previous, void* context); #endif diff --git a/tests/unittests/test_cmd_otr.c b/tests/unittests/test_cmd_otr.c index 0aca077c..7dcc1767 100644 --- a/tests/unittests/test_cmd_otr.c +++ b/tests/unittests/test_cmd_otr.c @@ -271,6 +271,7 @@ test_cmd_otr_theirfp_from_wintype(win_type_t wintype) window.type = wintype; window.layout = NULL; window.urls_ac = NULL; + window.quotes_ac = NULL; will_return(connection_get_status, JABBER_CONNECTED); @@ -308,6 +309,7 @@ cmd_otr_theirfp_shows_message_when_non_otr_chat_window(void** state) window.type = WIN_CHAT; window.layout = NULL; window.urls_ac = NULL; + window.quotes_ac = NULL; ProfChatWin chatwin; chatwin.window = window; chatwin.memcheck = PROFCHATWIN_MEMCHECK; @@ -337,6 +339,7 @@ cmd_otr_theirfp_shows_fingerprint(void** state) window.type = WIN_CHAT; window.layout = NULL; window.urls_ac = NULL; + window.quotes_ac = NULL; ProfChatWin chatwin; chatwin.window = window; chatwin.barejid = recipient; @@ -365,6 +368,7 @@ test_cmd_otr_start_from_wintype(win_type_t wintype) window.type = wintype; window.layout = NULL; window.urls_ac = NULL; + window.quotes_ac = NULL; will_return(connection_get_status, JABBER_CONNECTED); @@ -404,6 +408,7 @@ cmd_otr_start_shows_message_when_already_started(void** state) window.type = WIN_CHAT; window.layout = NULL; window.urls_ac = NULL; + window.quotes_ac = NULL; ProfChatWin chatwin; chatwin.window = window; chatwin.barejid = recipient; @@ -430,6 +435,7 @@ cmd_otr_start_shows_message_when_no_key(void** state) window.type = WIN_CHAT; window.layout = NULL; window.urls_ac = NULL; + window.quotes_ac = NULL; ProfChatWin chatwin; chatwin.window = window; chatwin.barejid = recipient; @@ -454,6 +460,7 @@ cmd_otr_start_sends_otr_query_message_to_current_recipeint(void** state) window.type = WIN_CHAT; window.layout = NULL; window.urls_ac = NULL; + window.quotes_ac = NULL; ProfChatWin chatwin; chatwin.window = window; chatwin.barejid = recipient; From fb790a955be29184a376a5d579ad9378718c280f Mon Sep 17 00:00:00 2001 From: MarcoPolo-PasTonMolo Date: Mon, 21 Mar 2022 14:52:17 +0200 Subject: [PATCH 2/2] Use funcs available in all glib versions to replace \n in quotes --- src/ui/window.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ui/window.c b/src/ui/window.c index 516e2209..7952497c 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -2033,12 +2033,17 @@ win_quote_autocomplete(ProfWin* window, const char* const input, gboolean previo return NULL; } + gchar **parts = g_strsplit(result, "\n", -1); + gchar *quoted_result = g_strjoinv("\n> ", parts); + GString* replace_with = g_string_new("> "); - g_string_append(replace_with, result); - g_string_replace(replace_with, "\n", "\n> ", 0); + g_string_append(replace_with, quoted_result); g_string_append(replace_with, "\n"); g_free(result); + g_free(quoted_result); + g_strfreev(parts); + result = replace_with->str; g_string_free(replace_with, FALSE); return result;