From b6cc42cbf7432041de9087dfa955a3d8d62af709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Tue, 28 Apr 2020 11:15:30 +0200 Subject: [PATCH] relay: add command "completion" in weechat relay protocol (closes #1484) --- ChangeLog.adoc | 1 + doc/en/weechat_relay_protocol.en.adoc | 152 +++++++++++++-- doc/fr/weechat_relay_protocol.fr.adoc | 152 +++++++++++++-- doc/ja/weechat_relay_protocol.ja.adoc | 154 ++++++++++++++-- .../relay/weechat/relay-weechat-protocol.c | 173 ++++++++++++++++++ 5 files changed, 599 insertions(+), 33 deletions(-) diff --git a/ChangeLog.adoc b/ChangeLog.adoc index c9836a574..6cd2085fd 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -29,6 +29,7 @@ New features:: * buflist: add options enable/disable/toggle in command /buflist * buflist: evaluate option buflist.look.sort so that sort can be customized for each of the three buflist bar items (issue #1465) * relay: add command "handshake" in weechat relay protocol and nonce to prevent replay attacks, add options relay.network.password_hash_algo, relay.network.password_hash_iterations, relay.network.nonce_size (issue #1474) + * relay: add command "completion" in weechat relay protocol to perform a completion on a string at a given position (issue #1484) * relay: add option relay.network.auth_timeout * relay: update default colors for client status * relay: add status "waiting_auth" in irc and weechat protocols (issue #1358) diff --git a/doc/en/weechat_relay_protocol.en.adoc b/doc/en/weechat_relay_protocol.en.adoc index f6b50fba9..7a0c70949 100644 --- a/doc/en/weechat_relay_protocol.en.adoc +++ b/doc/en/weechat_relay_protocol.en.adoc @@ -86,17 +86,18 @@ List of available commands (detail in next chapters): [width="100%",cols="^3m,14",options="header"] |=== -| Command | Description -| handshake | Handshake: prepare client authentication and set options, before _init_ command. -| init | Authenticate with _relay_. -| hdata | Request a _hdata_. -| info | Request an _info_. -| infolist | Request an _infolist_. -| nicklist | Request a _nicklist_. -| input | Send data to a buffer (text or command). -| sync | Synchronize buffer(s): get updates for buffer(s). -| desync | Desynchronize buffer(s): stop updates for buffer(s). -| quit | Disconnect from _relay_. +| Command | Description +| handshake | Handshake: prepare client authentication and set options, before _init_ command. +| init | Authenticate with _relay_. +| hdata | Request a _hdata_. +| info | Request an _info_. +| infolist | Request an _infolist_. +| nicklist | Request a _nicklist_. +| input | Send data to a buffer (text or command). +| completion | Request completion of a string. +| sync | Synchronize buffer(s): get updates for buffer(s). +| desync | Desynchronize buffer(s): stop updates for buffer(s). +| quit | Disconnect from _relay_. |=== [[command_handshake]] @@ -447,6 +448,135 @@ input core.weechat /help filter input irc.freenode.#weechat hello! ---- +[[command_completion]] +=== completion + +_WeeChat ≥ 2.9._ + +Request completion of a string: list of possible words at a given position in +a string for a given buffer. + +Syntax: + +---- +(id) completion [] +---- + +Arguments: + +* _buffer_: pointer (_0x12345_) or full name of buffer (for example: + _core.weechat_ or _irc.freenode.#weechat_) +* _position_: position for completion in string (starts to 0); + if the value is -1, the position is the length of _data_ (so the completion + is made at the end of _data_) +* _data_: the input string; if not given, completion is performed on an empty + string + +WeeChat replies with a hdata: + +[width="100%",cols="3m,2,10",options="header"] +|=== +| Name | Type | Description +| context | string | Completion context: "null" (no completion), "command", "command_arg", "auto". +| base_word | string | The base word used for completion. +| pos_start | integer | Index of first char to replace (starts to 0). +| pos_end | integer | Index of last char to replace (starts to 0). +| add_space | integer | 1 if a space must be added after words, 0 otherwise. +| list | array of strings | List of words found; empty if nothing was found to complete at asked position. +|=== + +[NOTE] +In case of error, for example invalid buffer or internal error on WeeChat side, +an empty hdata is returned. + +Example: completion of a command argument: + +---- +(test) completion core.weechat -1 /help fi +---- + +Response: + +[source,python] +---- +id: 'test' +hda: + keys: {'context': 'str', 'base_word': 'str', 'pos_start': 'int', 'pos_end': 'int', 'add_space': 'int', 'list': 'arr'} + path: ['completion'] + item 1: + __path: ['0x55d0ccc842c0'] + context: 'command_arg' + base_word: 'fi' + pos_start: 6 + pos_end: 7 + add_space: 0 + list: ['fifo', 'fifo.file.enabled', 'fifo.file.path', 'filter'] +---- + +Example: completion of a command in the middle of a word: + +---- +(test) completion core.weechat 5 /quernick +---- + +Response: + +[source,python] +---- +id: 'test' +hda: + keys: {'context': 'str', 'base_word': 'str', 'pos_start': 'int', 'pos_end': 'int', 'add_space': 'int', 'list': 'arr'} + path: ['completion'] + item 1: + __path: ['0x55d0ccc88470'] + context: 'command' + base_word: 'quer' + pos_start: 1 + pos_end: 4 + add_space: 1 + list: ['query'] +---- + +Example: nothing to complete: + +---- +(test) completion core.weechat -1 abcdefghijkl +---- + +Response: + +[source,python] +---- +id: 'test' +hda: + keys: {'context': 'str', 'base_word': 'str', 'pos_start': 'int', 'pos_end': 'int', 'add_space': 'int', 'list': 'arr'} + path: ['completion'] + item 1: + __path: ['0x55d0ccc88470'] + context: 'auto' + base_word: 'abcdefghijkl' + pos_start: 0 + pos_end: 11 + add_space: 1 + list: [] +---- + +Example: invalid buffer: + +---- +(test) completion buffer.does.not.exist -1 /help fi +---- + +Response: + +[source,python] +---- +id: 'test' +hda: + keys: {} + path: ['completion'] +---- + [[command_sync]] === sync diff --git a/doc/fr/weechat_relay_protocol.fr.adoc b/doc/fr/weechat_relay_protocol.fr.adoc index c2c63395d..f30621afe 100644 --- a/doc/fr/weechat_relay_protocol.fr.adoc +++ b/doc/fr/weechat_relay_protocol.fr.adoc @@ -91,17 +91,18 @@ Liste des commandes disponibles (détail dans les chapitres suivants) : [width="100%",cols="^3m,14",options="header"] |=== -| Commande | Description -| handshake | Poignée de main : préparer l'authentification du client et définir des options, avant la commande _init_. -| init | S'authentifier avec _relay_. -| hdata | Demander un _hdata_. -| info | Demander une _info_. -| infolist | Demander une _infolist_. -| nicklist | Demander une _nicklist_ (liste de pseudos). -| input | Envoyer des données à un tampon (texte ou commande). -| sync | Synchroniser un/des tampon(s) : recevoir les mises à jour pour le(s) tampon(s). -| desync | Désynchroniser un/des tampon(s) : stopper les mises à jour pour le(s) tampon(s). -| quit | Se déconnecter de _relay_. +| Commande | Description +| handshake | Poignée de main : préparer l'authentification du client et définir des options, avant la commande _init_. +| init | S'authentifier avec _relay_. +| hdata | Demander un _hdata_. +| info | Demander une _info_. +| infolist | Demander une _infolist_. +| nicklist | Demander une _nicklist_ (liste de pseudos). +| input | Envoyer des données à un tampon (texte ou commande). +| completion | Demander la complétion d'une chaîne. +| sync | Synchroniser un/des tampon(s) : recevoir les mises à jour pour le(s) tampon(s). +| desync | Désynchroniser un/des tampon(s) : stopper les mises à jour pour le(s) tampon(s). +| quit | Se déconnecter de _relay_. |=== [[command_handshake]] @@ -464,6 +465,135 @@ input core.weechat /help filter input irc.freenode.#weechat bonjour ! ---- +[[command_completion]] +=== completion + +_WeeChat ≥ 2.9._ + +Demander la complétion d'une chaîne : liste des mots possibles à une position +donnée dans la chaîne et sur un tampon donné. + +Syntaxe : + +---- +(id) completion [] +---- + +Paramètres : + +* _tampon_ : pointeur (_0x12345_) ou nom complet du tampon (par exemple : + _core.weechat_ ou _irc.freenode.#weechat_) +* _position_ : position dans la chaîne pour la complétion (démarre à 0) ; + si la valeur est -1, la position est la longueur de _données_ (donc la + complétion se fait à la fin de _données_) +* _données_ : la chaîne en entrée ; si non donnée, la complétion est faite sur + une chaîne vide + +WeeChat répond avec un hdata : + +[width="100%",cols="3m,2,10",options="header"] +|=== +| Nom | Type | Description +| context | chaîne | Contexte de complétion : "null" (pas de complétion), "command", "command_arg", "auto". +| base_word | chaîne | Le mot de base utilisé pour la complétion. +| pos_start | entier | Index du premier caractère à remplacer (démarre à 0). +| pos_end | entier | Index du dernier caractère à remplacer (démarre à 0). +| add_space | entier | 1 si un espace doit être ajouté après les mods, 0 sinon. +| list | tableau de chaînes | Liste des mots ; vide si rien n'a été trouvé pour compléter à la position demandée. +|=== + +[NOTE] +En cas d'erreur, par exemple un tampon invalide ou une erreur interne du côté +de WeeChat, un hdata vide est retourné. + +Exemple : complétion d'un paramètre de commande : + +---- +(test) completion core.weechat -1 /help fi +---- + +Réponse : + +[source,python] +---- +id: 'test' +hda: + keys: {'context': 'str', 'base_word': 'str', 'pos_start': 'int', 'pos_end': 'int', 'add_space': 'int', 'list': 'arr'} + path: ['completion'] + item 1: + __path: ['0x55d0ccc842c0'] + context: 'command_arg' + base_word: 'fi' + pos_start: 6 + pos_end: 7 + add_space: 0 + list: ['fifo', 'fifo.file.enabled', 'fifo.file.path', 'filter'] +---- + +Exemple : complétion d'une commande au milieu d'un mot : + +---- +(test) completion core.weechat 5 /quernick +---- + +Réponse : + +[source,python] +---- +id: 'test' +hda: + keys: {'context': 'str', 'base_word': 'str', 'pos_start': 'int', 'pos_end': 'int', 'add_space': 'int', 'list': 'arr'} + path: ['completion'] + item 1: + __path: ['0x55d0ccc88470'] + context: 'command' + base_word: 'quer' + pos_start: 1 + pos_end: 4 + add_space: 1 + list: ['query'] +---- + +Exemple : rien à compléter : + +---- +(test) completion core.weechat -1 abcdefghijkl +---- + +Réponse : + +[source,python] +---- +id: 'test' +hda: + keys: {'context': 'str', 'base_word': 'str', 'pos_start': 'int', 'pos_end': 'int', 'add_space': 'int', 'list': 'arr'} + path: ['completion'] + item 1: + __path: ['0x55d0ccc88470'] + context: 'auto' + base_word: 'abcdefghijkl' + pos_start: 0 + pos_end: 11 + add_space: 1 + list: [] +---- + +Exemple : tampon invalide : + +---- +(test) completion buffer.does.not.exist -1 /help fi +---- + +Réponse : + +[source,python] +---- +id: 'test' +hda: + keys: {} + path: ['completion'] +---- + [[command_sync]] === sync diff --git a/doc/ja/weechat_relay_protocol.ja.adoc b/doc/ja/weechat_relay_protocol.ja.adoc index 77e0bbf08..a52aee046 100644 --- a/doc/ja/weechat_relay_protocol.ja.adoc +++ b/doc/ja/weechat_relay_protocol.ja.adoc @@ -93,18 +93,20 @@ _リレー_ プラグインは _IRC プロキシ_ のように振舞います ( [width="100%",cols="^3m,14",options="header"] |=== -| コマンド | 説明 +| コマンド | 説明 // TRANSLATION MISSING -| handshake | Handshake: prepare client authentication and set options, before _init_ command. -| init | _リレー_ 接続を初期化 -| hdata | _hdata_ を要求 -| info | _インフォ_ を要求 -| infolist | _インフォリスト_ を要求 -| nicklist | _ニックネームリスト_ を要求 -| input | バッファにデータを送信 (テキストまたはコマンド) -| sync | バッファを同期: バッファの最新情報を取得 -| desync | バッファを非同期: バッファの更新を止める -| quit | _リレー_ から切断 +| handshake | Handshake: prepare client authentication and set options, before _init_ command. +| init | _リレー_ 接続を初期化 +| hdata | _hdata_ を要求 +| info | _インフォ_ を要求 +| infolist | _インフォリスト_ を要求 +| nicklist | _ニックネームリスト_ を要求 +| input | バッファにデータを送信 (テキストまたはコマンド) +// TRANSLATION MISSING +| completion | Request completion of a string. +| sync | バッファを同期: バッファの最新情報を取得 +| desync | バッファを非同期: バッファの更新を止める +| quit | _リレー_ から切断 |=== // TRANSLATION MISSING @@ -462,6 +464,136 @@ input core.weechat /help filter input irc.freenode.#weechat hello! ---- +// TRANSLATION MISSING +[[command_completion]] +=== completion + +_WeeChat ≥ 2.9._ + +Request completion of a string: list of possible words at a given position in +a string for a given buffer. + +構文: + +---- +(id) completion [] +---- + +引数: + +* _buffer_: pointer (_0x12345_) or full name of buffer (for example: + _core.weechat_ or _irc.freenode.#weechat_) +* _position_: position for completion in string (starts to 0); + if the value is -1, the position is the length of _data_ (so the completion + is made at the end of _data_) +* _data_: the input string; if not given, completion is performed on an empty + string + +WeeChat replies with a hdata: + +[width="100%",cols="3m,2,10",options="header"] +|=== +| Name | Type | Description +| context | string | Completion context: "null" (no completion), "command", "command_arg", "auto". +| base_word | string | The base word used for completion. +| pos_start | integer | Index of first char to replace (starts to 0). +| pos_end | integer | Index of last char to replace (starts to 0). +| add_space | integer | 1 if a space must be added after words, 0 otherwise. +| list | array of strings | List of words found; empty if nothing was found to complete at asked position. +|=== + +[NOTE] +In case of error, for example invalid buffer or internal error on WeeChat side, +an empty hdata is returned. + +Example: completion of a command argument: + +---- +(test) completion core.weechat -1 /help fi +---- + +Response: + +[source,python] +---- +id: 'test' +hda: + keys: {'context': 'str', 'base_word': 'str', 'pos_start': 'int', 'pos_end': 'int', 'add_space': 'int', 'list': 'arr'} + path: ['completion'] + item 1: + __path: ['0x55d0ccc842c0'] + context: 'command_arg' + base_word: 'fi' + pos_start: 6 + pos_end: 7 + add_space: 0 + list: ['fifo', 'fifo.file.enabled', 'fifo.file.path', 'filter'] +---- + +Example: completion of a command in the middle of a word: + +---- +(test) completion core.weechat 5 /quernick +---- + +Response: + +[source,python] +---- +id: 'test' +hda: + keys: {'context': 'str', 'base_word': 'str', 'pos_start': 'int', 'pos_end': 'int', 'add_space': 'int', 'list': 'arr'} + path: ['completion'] + item 1: + __path: ['0x55d0ccc88470'] + context: 'command' + base_word: 'quer' + pos_start: 1 + pos_end: 4 + add_space: 1 + list: ['query'] +---- + +Example: nothing to complete: + +---- +(test) completion core.weechat -1 abcdefghijkl +---- + +Response: + +[source,python] +---- +id: 'test' +hda: + keys: {'context': 'str', 'base_word': 'str', 'pos_start': 'int', 'pos_end': 'int', 'add_space': 'int', 'list': 'arr'} + path: ['completion'] + item 1: + __path: ['0x55d0ccc88470'] + context: 'auto' + base_word: 'abcdefghijkl' + pos_start: 0 + pos_end: 11 + add_space: 1 + list: [] +---- + +Example: invalid buffer: + +---- +(test) completion buffer.does.not.exist -1 /help fi +---- + +Response: + +[source,python] +---- +id: 'test' +hda: + keys: {} + path: ['completion'] +---- + [[command_sync]] === sync diff --git a/src/plugins/relay/weechat/relay-weechat-protocol.c b/src/plugins/relay/weechat/relay-weechat-protocol.c index 91abec80b..55325df23 100644 --- a/src/plugins/relay/weechat/relay-weechat-protocol.c +++ b/src/plugins/relay/weechat/relay-weechat-protocol.c @@ -664,6 +664,178 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(input) return WEECHAT_RC_OK; } +/* + * Callback for command "completion" (from client). + * + * Message looks like: + * completion core.weechat -1 /help fi + * input irc.freenode.#weechat 5 /quernick + * input 0x12345678 -1 nick + */ + +RELAY_WEECHAT_PROTOCOL_CALLBACK(completion) +{ + struct t_gui_buffer *ptr_buffer; + struct t_gui_completion *completion; + struct t_gui_completion_word *word; + struct t_hdata *ptr_hdata_completion, *ptr_hdata_completion_word; + struct t_arraylist *ptr_list; + struct t_relay_weechat_msg *msg; + char *error, *pos_data; + int i, position, length_data, context, pos_start, size; + + RELAY_WEECHAT_PROTOCOL_MIN_ARGS(0); + + completion = NULL; + + /* return an empty hdata as error if there are not enough arguments */ + if (argc < 2) + goto error; + + ptr_buffer = relay_weechat_protocol_get_buffer (argv[0]); + if (!ptr_buffer) + { + if (weechat_relay_plugin->debug >= 1) + { + weechat_printf (NULL, + _("%s: invalid buffer in message: \"%s %s\""), + RELAY_PLUGIN_NAME, + command, + argv[0]); + } + goto error; + } + + error = NULL; + position = (int)strtol (argv[1], &error, 10); + if (!error || error[0]) + goto error; + + pos_data = strchr (argv_eol[1], ' '); + if (pos_data) + pos_data++; + + length_data = strlen ((pos_data) ? pos_data : ""); + if ((position < 0) || (position > length_data)) + position = length_data; + + completion = weechat_completion_new (ptr_buffer); + if (!completion) + goto error; + + if (!weechat_completion_search (completion, + (pos_data) ? pos_data : "", + position, + 1)) + { + goto error; + } + + ptr_hdata_completion = weechat_hdata_get ("completion"); + if (!ptr_hdata_completion) + goto error; + + ptr_hdata_completion_word = weechat_hdata_get ("completion_word"); + if (!ptr_hdata_completion_word) + goto error; + + ptr_list = weechat_hdata_pointer (ptr_hdata_completion, completion, "list"); + if (!ptr_list) + goto error; + + msg = relay_weechat_msg_new (id); + if (msg) + { + relay_weechat_msg_add_type (msg, RELAY_WEECHAT_MSG_OBJ_HDATA); + relay_weechat_msg_add_string (msg, "completion"); + relay_weechat_msg_add_string (msg, + "context:str," + "base_word:str," + "pos_start:int," + "pos_end:int," + "add_space:int," + "list:arr"); + relay_weechat_msg_add_int (msg, 1); /* count */ + relay_weechat_msg_add_pointer (msg, completion); + /* context */ + context = weechat_hdata_integer (ptr_hdata_completion, completion, + "context"); + switch (context) + { + case 0: + relay_weechat_msg_add_string (msg, "null"); + break; + case 1: + relay_weechat_msg_add_string (msg, "command"); + break; + case 2: + relay_weechat_msg_add_string (msg, "command_arg"); + break; + default: + relay_weechat_msg_add_string (msg, "auto"); + break; + } + /* base_word */ + relay_weechat_msg_add_string ( + msg, + weechat_hdata_string (ptr_hdata_completion, + completion, "base_word")); + /* pos_start */ + pos_start = weechat_hdata_integer (ptr_hdata_completion, + completion, "position_replace"); + relay_weechat_msg_add_int (msg, pos_start); + + /* pos_end */ + relay_weechat_msg_add_int ( + msg, + (position > pos_start) ? position - 1 : position); + /* add_space */ + relay_weechat_msg_add_int ( + msg, + weechat_hdata_integer (ptr_hdata_completion, + completion, "add_space")); + /* list */ + relay_weechat_msg_add_type (msg, RELAY_WEECHAT_MSG_OBJ_STRING); + size = weechat_arraylist_size (ptr_list); + relay_weechat_msg_add_int (msg, size); + for (i = 0; i < size; i++) + { + word = (struct t_gui_completion_word *)weechat_arraylist_get ( + ptr_list, i); + relay_weechat_msg_add_string ( + msg, + weechat_hdata_string (ptr_hdata_completion_word, word, "word")); + } + + /* send message */ + relay_weechat_msg_send (client, msg); + relay_weechat_msg_free (msg); + } + + weechat_completion_free (completion); + + return WEECHAT_RC_OK; + +error: + if (completion) + weechat_completion_free (completion); + + msg = relay_weechat_msg_new (id); + if (msg) + { + relay_weechat_msg_add_type (msg, RELAY_WEECHAT_MSG_OBJ_HDATA); + relay_weechat_msg_add_string (msg, "completion"); + relay_weechat_msg_add_string (msg, NULL); /* keys */ + relay_weechat_msg_add_int (msg, 0); /* count */ + + /* send message */ + relay_weechat_msg_send (client, msg); + relay_weechat_msg_free (msg); + } + + return WEECHAT_RC_OK; +} + /* * Callback for signals "buffer_*". */ @@ -1565,6 +1737,7 @@ relay_weechat_protocol_recv (struct t_relay_client *client, const char *data) { "infolist", &relay_weechat_protocol_cb_infolist }, { "nicklist", &relay_weechat_protocol_cb_nicklist }, { "input", &relay_weechat_protocol_cb_input }, + { "completion", &relay_weechat_protocol_cb_completion }, { "sync", &relay_weechat_protocol_cb_sync }, { "desync", &relay_weechat_protocol_cb_desync }, { "test", &relay_weechat_protocol_cb_test },