mirror of
https://github.com/Tha14/toxic.git
synced 2024-12-23 02:33:25 +01:00
auto-completion for paths when sending file & improved auto-complete algorithm to do partial matches
This commit is contained in:
parent
e61d070def
commit
ea3fcd5b79
@ -22,7 +22,7 @@ CFLAGS += $(USER_CFLAGS)
|
||||
LDFLAGS = $(USER_LDFLAGS)
|
||||
|
||||
OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_senders.o
|
||||
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o
|
||||
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
|
||||
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o
|
||||
|
||||
# Variables for audio support
|
||||
|
251
src/autocomplete.c
Normal file
251
src/autocomplete.c
Normal file
@ -0,0 +1,251 @@
|
||||
/* autocomplete.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/types.h>
|
||||
#include <sys/dir.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif /* ifdef __APPLE__ */
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "misc_tools.h"
|
||||
#include "line_info.h"
|
||||
#include "execute.h"
|
||||
|
||||
/* puts match in match buffer. if more than one match, add first n chars that are identical.
|
||||
e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */
|
||||
static void get_str_match(char *match, char (*matches)[MAX_STR_SIZE], int n)
|
||||
{
|
||||
if (n == 1) {
|
||||
strcpy(match, matches[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
int shortest = MAX_STR_SIZE;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
int m_len = strlen(matches[i]);
|
||||
|
||||
if (m_len < shortest)
|
||||
shortest = m_len;
|
||||
}
|
||||
|
||||
for (i = 0; i < shortest; ++i) {
|
||||
char ch = matches[0][i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < n; ++j) {
|
||||
if (matches[j][i] != ch) {
|
||||
strcpy(match, matches[0]);
|
||||
match[i] = '\0';
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(match, matches[0]);
|
||||
}
|
||||
|
||||
/* looks for the first instance in list that begins with the last entered word in line according to pos,
|
||||
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
with "Hello john". Works slightly differently for directory paths with the same results.
|
||||
|
||||
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||
in the list, and size is the size of each item in the list.
|
||||
|
||||
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||
int complete_line(ChatContext *ctx, const void *list, int n_items, int size)
|
||||
{
|
||||
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
const char *L = (char *) list;
|
||||
|
||||
bool dir_search = false;
|
||||
const char *endchrs = " ";
|
||||
char ubuf[MAX_STR_SIZE];
|
||||
|
||||
/* work with multibyte string copy of buf for simplicity */
|
||||
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
|
||||
return -1;
|
||||
|
||||
/* isolate substring from space behind pos to pos */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
||||
tmp[ctx->pos] = '\0';
|
||||
|
||||
const char *s = strrchr(tmp, ' ');
|
||||
char *sub = malloc(strlen(ubuf) + 1);
|
||||
|
||||
if (sub == NULL)
|
||||
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
|
||||
|
||||
if (!s) {
|
||||
strcpy(sub, tmp);
|
||||
|
||||
if (sub[0] != '/')
|
||||
endchrs = ": ";
|
||||
} else {
|
||||
strcpy(sub, &s[1]);
|
||||
|
||||
if (strncmp(ubuf, "/sendfile", strlen("/sendfile")) == 0) {
|
||||
dir_search = true;
|
||||
int sub_len = strlen(sub);
|
||||
int si = char_rfind(sub, '/', sub_len);
|
||||
memmove(sub, &sub[si + 1], sub_len - si);
|
||||
}
|
||||
}
|
||||
|
||||
if (string_is_empty(sub)) {
|
||||
free(sub);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int s_len = strlen(sub);
|
||||
const char *str;
|
||||
int n_matches = 0;
|
||||
char matches[n_items][MAX_STR_SIZE];
|
||||
int i = 0;
|
||||
|
||||
/* put all list matches in matches array */
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
str = &L[i * size];
|
||||
|
||||
if (strncasecmp(str, sub, s_len) == 0)
|
||||
strcpy(matches[n_matches++], str);
|
||||
}
|
||||
|
||||
free(sub);
|
||||
|
||||
if (!n_matches)
|
||||
return -1;
|
||||
|
||||
char match[size];
|
||||
get_str_match(match, matches, n_matches);
|
||||
|
||||
if (dir_search) {
|
||||
if (n_matches == 1)
|
||||
endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/";
|
||||
else
|
||||
endchrs = "";
|
||||
} else if (n_matches > 1) {
|
||||
endchrs = "";
|
||||
}
|
||||
|
||||
/* put match in correct spot in buf and append endchars */
|
||||
int n_endchrs = strlen(endchrs);
|
||||
int m_len = strlen(match);
|
||||
int strt = ctx->pos - s_len;
|
||||
int diff = m_len - s_len + n_endchrs;
|
||||
|
||||
if (ctx->len + diff > MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
char tmpend[MAX_STR_SIZE];
|
||||
strcpy(tmpend, &ubuf[ctx->pos]);
|
||||
strcpy(&ubuf[strt], match);
|
||||
strcpy(&ubuf[strt + m_len], endchrs);
|
||||
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
|
||||
|
||||
/* convert to widechar and copy back to original buf */
|
||||
wchar_t newbuf[MAX_STR_SIZE];
|
||||
|
||||
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
|
||||
return -1;
|
||||
|
||||
wcscpy(ctx->line, newbuf);
|
||||
|
||||
ctx->len += diff;
|
||||
ctx->pos += diff;
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
/* matches /sendfile "<incomplete-dir>" line to matching directories.
|
||||
|
||||
if only one match, auto-complete line and return diff between old len and new len.
|
||||
return 0 if > 1 match and print out all the matches
|
||||
return -1 if no matches */
|
||||
|
||||
#define MAX_DIRS 256
|
||||
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line)
|
||||
{
|
||||
char b_path[MAX_STR_SIZE];
|
||||
char b_name[MAX_STR_SIZE];
|
||||
|
||||
if (wcs_to_mbs_buf(b_path, line, sizeof(b_path)) == -1)
|
||||
return -1;
|
||||
|
||||
int si = char_rfind(b_path, '/', strlen(b_path));
|
||||
|
||||
if (!b_path[0]) { /* list everything in pwd */
|
||||
strcpy(b_path, ".");
|
||||
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), ".%s", b_path);
|
||||
strcpy(b_path, tmp);
|
||||
}
|
||||
|
||||
strcpy(b_name, &b_path[si + 1]);
|
||||
b_path[si + 1] = '\0';
|
||||
int b_name_len = strlen(b_name);
|
||||
|
||||
DIR *dp = opendir(b_path);
|
||||
|
||||
if (dp == NULL)
|
||||
return -1;
|
||||
|
||||
char dirnames[MAX_DIRS][NAME_MAX];
|
||||
struct dirent *entry;
|
||||
int dircount = 0;
|
||||
|
||||
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
|
||||
if (strncmp(entry->d_name, b_name, b_name_len) == 0) {
|
||||
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
|
||||
++dircount;
|
||||
}
|
||||
}
|
||||
|
||||
if (dircount == 0)
|
||||
return -1;
|
||||
|
||||
if (dircount > 1) {
|
||||
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dircount; ++i)
|
||||
line_info_add(self, NULL, NULL, NULL, dirnames[i], SYS_MSG, 0, 0);
|
||||
|
||||
complete_line(self->chatwin, dirnames, dircount, NAME_MAX);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return complete_line(self->chatwin, dirnames, dircount, NAME_MAX);
|
||||
}
|
43
src/autocomplete.h
Normal file
43
src/autocomplete.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* autocomplete.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _autocomplete_h
|
||||
#define _autocomplete_h
|
||||
|
||||
/* looks for the first instance in list that begins with the last entered word in line according to pos,
|
||||
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
with "Hello john". Works slightly differently for directory paths with the same results.
|
||||
|
||||
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||
in the list, and size is the size of each item in the list.
|
||||
|
||||
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||
int complete_line(ChatContext *ctx, const void *list, int n_items, int size);
|
||||
|
||||
/* matches /sendfile "<incomplete-dir>" line to matching directories.
|
||||
|
||||
if only one match, auto-complete line and return diff between old len and new len.
|
||||
return 0 if > 1 match and print out all the matches
|
||||
return -1 if no matches */
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line);
|
||||
|
||||
#endif /* #define _autocomplete_h */
|
28
src/chat.c
28
src/chat.c
@ -41,6 +41,7 @@
|
||||
#include "settings.h"
|
||||
#include "input.h"
|
||||
#include "help.h"
|
||||
#include "autocomplete.h"
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#include "audio_call.h"
|
||||
@ -685,21 +686,22 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
|
||||
input_handle(self, key, x, y, x2, y2);
|
||||
|
||||
if (key == '\t') { /* TAB key: auto-completes command */
|
||||
if (ctx->len > 1 && ctx->line[0] == '/') {
|
||||
int diff = complete_line(ctx, chat_cmd_list, AC_NUM_CHAT_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
if (key == '\t' && ctx->len > 1 && ctx->line[0] == '/') { /* TAB key: auto-complete */
|
||||
int diff = -1;
|
||||
int sf_len = 11;
|
||||
|
||||
if (diff != -1) {
|
||||
if (x + diff > x2 - 1) {
|
||||
wmove(self->window, y, x + diff);
|
||||
ctx->start += diff;
|
||||
} else {
|
||||
wmove(self->window, y, x + diff);
|
||||
}
|
||||
} else
|
||||
beep();
|
||||
} else
|
||||
if (wcsncmp(ctx->line, L"/sendfile \"", sf_len) == 0) {
|
||||
diff = dir_match(self, m, &ctx->line[sf_len]);
|
||||
} else {
|
||||
diff = complete_line(ctx, chat_cmd_list, AC_NUM_CHAT_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
}
|
||||
|
||||
if (diff != -1) {
|
||||
if (x + diff > x2 - 1)
|
||||
ctx->start += diff;
|
||||
} else {
|
||||
beep();
|
||||
}
|
||||
|
||||
} else if (key == '\n') {
|
||||
rm_trailing_spaces_buf(ctx);
|
||||
|
@ -26,9 +26,9 @@
|
||||
#include <resolv.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <arpa/nameser_compat.h>
|
||||
#include <arpa/nameser_compat.h>
|
||||
#else
|
||||
#include <arpa/nameser.h>
|
||||
#include <arpa/nameser.h>
|
||||
#endif /* ifdef __APPLE__ */
|
||||
|
||||
#include <tox/toxdns.h>
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "settings.h"
|
||||
#include "input.h"
|
||||
#include "help.h"
|
||||
#include "autocomplete.h"
|
||||
|
||||
extern char *DATA_FILE;
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
@ -247,7 +248,21 @@ int char_find(int idx, const char *s, char ch)
|
||||
{
|
||||
int i = idx;
|
||||
|
||||
for ( ; s[i]; ++i) {
|
||||
for (i = idx; s[i]; ++i) {
|
||||
if (s[i] == ch)
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* returns index of the last instance of ch in s starting at len
|
||||
returns 0 if char not found (skips 0th index) */
|
||||
int char_rfind(const char *s, char ch, int len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = len; i > 0; --i) {
|
||||
if (s[i] == ch)
|
||||
break;
|
||||
}
|
||||
|
@ -93,4 +93,8 @@ int get_nick_truncate(Tox *m, char *buf, int friendnum);
|
||||
returns length of s if char not found */
|
||||
int char_find(int idx, const char *s, char ch);
|
||||
|
||||
/* returns index of the last instance of ch in s
|
||||
returns 0 if char not found */
|
||||
int char_rfind(const char *s, char ch, int len);
|
||||
|
||||
#endif /* #define _misc_tools_h */
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "settings.h"
|
||||
#include "input.h"
|
||||
#include "help.h"
|
||||
#include "autocomplete.h"
|
||||
|
||||
char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
||||
uint16_t num_frnd_requests = 0;
|
||||
|
@ -208,97 +208,3 @@ void fetch_hist_item(ChatContext *ctx, int key_dir)
|
||||
ctx->pos = h_len;
|
||||
ctx->len = h_len;
|
||||
}
|
||||
|
||||
/* looks for the first instance in list that begins with the last entered word in line according to pos,
|
||||
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
with "Hello john".
|
||||
|
||||
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||
in the list, and size is the size of each item in the list.
|
||||
|
||||
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||
int complete_line(ChatContext *ctx, const void *list, int n_items, int size)
|
||||
{
|
||||
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
const char *L = (char *) list;
|
||||
|
||||
char ubuf[MAX_STR_SIZE];
|
||||
|
||||
/* work with multibyte string copy of buf for simplicity */
|
||||
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
|
||||
return -1;
|
||||
|
||||
/* isolate substring from space behind pos to pos */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
||||
tmp[ctx->pos] = '\0';
|
||||
const char *s = strrchr(tmp, ' ');
|
||||
int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */
|
||||
|
||||
char *sub = malloc(strlen(ubuf) + 1);
|
||||
|
||||
if (sub == NULL)
|
||||
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
|
||||
|
||||
if (!s) {
|
||||
strcpy(sub, tmp);
|
||||
|
||||
if (sub[0] != '/') /* make sure it's not a command */
|
||||
n_endchrs = 2;
|
||||
} else {
|
||||
strcpy(sub, &s[1]);
|
||||
}
|
||||
|
||||
if (string_is_empty(sub)) {
|
||||
free(sub);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int s_len = strlen(sub);
|
||||
const char *match;
|
||||
bool is_match = false;
|
||||
int i;
|
||||
|
||||
/* look for a match in list */
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
match = &L[i * size];
|
||||
|
||||
if ((is_match = strncasecmp(match, sub, s_len) == 0))
|
||||
break;
|
||||
}
|
||||
|
||||
free(sub);
|
||||
|
||||
if (!is_match)
|
||||
return -1;
|
||||
|
||||
/* put match in correct spot in buf and append endchars (space or ": ") */
|
||||
const char *endchrs = n_endchrs == 1 ? " " : ": ";
|
||||
int m_len = strlen(match);
|
||||
int strt = ctx->pos - s_len;
|
||||
int diff = m_len - s_len + n_endchrs;
|
||||
|
||||
if (ctx->len + diff > MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
char tmpend[MAX_STR_SIZE];
|
||||
strcpy(tmpend, &ubuf[ctx->pos]);
|
||||
strcpy(&ubuf[strt], match);
|
||||
strcpy(&ubuf[strt + m_len], endchrs);
|
||||
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
|
||||
|
||||
/* convert to widechar and copy back to original buf */
|
||||
wchar_t newbuf[MAX_STR_SIZE];
|
||||
|
||||
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
|
||||
return -1;
|
||||
|
||||
wcscpy(ctx->line, newbuf);
|
||||
|
||||
ctx->len += diff;
|
||||
ctx->pos += diff;
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
@ -52,16 +52,6 @@ int yank_buf(ChatContext *ctx);
|
||||
/* Removes trailing spaces from line. */
|
||||
void rm_trailing_spaces_buf(ChatContext *ctx);
|
||||
|
||||
/* looks for the first instance in list that begins with the last entered word in line according to pos,
|
||||
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
with "Hello john".
|
||||
|
||||
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||
in the list, and size is the size of each item in the list.
|
||||
|
||||
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||
int complete_line(ChatContext *ctx, const void *list, int n_items, int size);
|
||||
|
||||
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. */
|
||||
void add_line_to_hist(ChatContext *ctx);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user