preliminary autocomplete, for commands only for now
This commit is contained in:
		
							parent
							
								
									24e3cb62bc
								
							
						
					
					
						commit
						c5c647b054
					
				| @ -7,6 +7,7 @@ PKG_CHECK_MODULES(CONFUSE REQUIRED libconfuse) | ||||
| SET(TARGET "ncdc") | ||||
| 
 | ||||
| SET(SOURCES | ||||
|   "include/ncdc/autocomplete.h" | ||||
|   "include/ncdc/cmds.h" | ||||
|   "include/ncdc/config.h" | ||||
|   "include/ncdc/input.h" | ||||
| @ -15,6 +16,7 @@ SET(SOURCES | ||||
|   "include/ncdc/textview.h" | ||||
|   "include/ncdc/treeview.h" | ||||
|   "src/ack.c" | ||||
|   "src/autocomplete.c" | ||||
|   "src/cmds.c" | ||||
|   "src/config.c" | ||||
|   "src/close.c" | ||||
|  | ||||
							
								
								
									
										40
									
								
								ncdc/include/ncdc/autocomplete.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								ncdc/include/ncdc/autocomplete.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| /*
 | ||||
|  * Part of ncdc - a discord client for the console | ||||
|  * Copyright (C) 2019 Florian Stinglmayr <fstinglmayr@gmail.com> | ||||
|  * | ||||
|  * This program 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. | ||||
|  * | ||||
|  * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef NCDC_AUTOCOMPLETE_H | ||||
| #define NCDC_AUTOCOMPLETE_H | ||||
| 
 | ||||
| #include <ncdc/ncdc.h> | ||||
| 
 | ||||
| struct ncdc_autocomplete_; | ||||
| typedef struct ncdc_autocomplete_ *ncdc_autocomplete_t; | ||||
| 
 | ||||
| ncdc_autocomplete_t ncdc_autocomplete_new(void); | ||||
| 
 | ||||
| bool ncdc_autocomplete_prepare(ncdc_autocomplete_t a, | ||||
|                                wchar_t const *s, ssize_t sz, | ||||
|                                size_t pos); | ||||
| void ncdc_autocomplete_completions(ncdc_autocomplete_t a, | ||||
|                                    wchar_t **words, ssize_t num); | ||||
| bool ncdc_autocomplete_complete(ncdc_autocomplete_t a, int *newpos); | ||||
| void ncdc_autocomplete_reset(ncdc_autocomplete_t a); | ||||
| 
 | ||||
| int ncdc_autocomplete_word_index(ncdc_autocomplete_t a); | ||||
| wchar_t const *ncdc_autocomplete_completed(ncdc_autocomplete_t a); | ||||
| 
 | ||||
| #endif | ||||
| @ -47,6 +47,8 @@ bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s); | ||||
|  * sub commands. for example usage see the friends command | ||||
|  */ | ||||
| ncdc_commands_t *ncdc_find_cmd(ncdc_commands_t *cmds, wchar_t const *name); | ||||
| wchar_t **ncdc_cmd_names(void); | ||||
| size_t ncdc_cmd_names_size(void); | ||||
| 
 | ||||
| bool ncdc_cmd_ack(ncdc_mainwindow_t n, size_t ac, wchar_t **av, wchar_t const *f); | ||||
| bool ncdc_cmd_close(ncdc_mainwindow_t n, size_t ac, wchar_t **av, wchar_t const *f); | ||||
|  | ||||
| @ -21,6 +21,7 @@ | ||||
| 
 | ||||
| #include <ncdc/ncdc.h> | ||||
| #include <ncdc/keycodes.h> | ||||
| #include <ncdc/autocomplete.h> | ||||
| 
 | ||||
| struct ncdc_input_; | ||||
| typedef struct ncdc_input_ *ncdc_input_t; | ||||
|  | ||||
| @ -89,9 +89,12 @@ char *read_char(FILE *stream); | ||||
| int aswprintf(wchar_t **buffer, wchar_t const *fmt, ...); | ||||
| char *w_convert(wchar_t const *w); | ||||
| wchar_t* wcsndup(const wchar_t* string, size_t maxlen); | ||||
| 
 | ||||
| size_t w_strlenv(wchar_t **s); | ||||
| void w_strfreev(wchar_t **s); | ||||
| wchar_t **w_strdupv(wchar_t **s, ssize_t sz); | ||||
| wchar_t *w_joinv(wchar_t const **v, size_t len); | ||||
| 
 | ||||
| wchar_t **w_tokenise(wchar_t const *w); | ||||
| wchar_t *w_next_tok(wchar_t const *w); | ||||
| wchar_t const *w_next_word(wchar_t const *w, ssize_t len); | ||||
|  | ||||
							
								
								
									
										215
									
								
								ncdc/src/autocomplete.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								ncdc/src/autocomplete.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,215 @@ | ||||
| /*
 | ||||
|  * Part of ncdc - a discord client for the console | ||||
|  * Copyright (C) 2019 Florian Stinglmayr <fstinglmayr@gmail.com> | ||||
|  * | ||||
|  * This program 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. | ||||
|  * | ||||
|  * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <ncdc/autocomplete.h> | ||||
| 
 | ||||
| struct ncdc_autocomplete_ | ||||
| { | ||||
|     dc_refable_t ref; | ||||
| 
 | ||||
|     wchar_t *buffer; | ||||
|     wchar_t *word; | ||||
|     wchar_t *completed; | ||||
| 
 | ||||
|     wchar_t **completions; | ||||
|     size_t completions_size; | ||||
|     size_t search_pos; | ||||
| 
 | ||||
|     size_t buffer_size; | ||||
|     size_t pos; | ||||
|     size_t word_pos; | ||||
|     size_t start; | ||||
|     size_t end; | ||||
| }; | ||||
| 
 | ||||
| static void ncdc_autocomplete_free(ncdc_autocomplete_t a) | ||||
| { | ||||
|     return_if_true(a == NULL,); | ||||
| 
 | ||||
|     free(a->buffer); | ||||
|     a->buffer = NULL; | ||||
| 
 | ||||
|     free(a->completed); | ||||
|     a->completed = NULL; | ||||
| 
 | ||||
|     free(a->word); | ||||
|     a->word = NULL; | ||||
| 
 | ||||
|     w_strfreev(a->completions); | ||||
|     a->completions = NULL; | ||||
| 
 | ||||
|     free(a); | ||||
| } | ||||
| 
 | ||||
| ncdc_autocomplete_t ncdc_autocomplete_new(void) | ||||
| { | ||||
|     ncdc_autocomplete_t c = calloc(1, sizeof(struct ncdc_autocomplete_)); | ||||
|     return_if_true(c == NULL, NULL); | ||||
| 
 | ||||
|     c->ref.cleanup = (dc_cleanup_t)ncdc_autocomplete_free; | ||||
| 
 | ||||
|     return dc_ref(c); | ||||
| } | ||||
| 
 | ||||
| void ncdc_autocomplete_reset(ncdc_autocomplete_t a) | ||||
| { | ||||
|     return_if_true(a == NULL,); | ||||
| 
 | ||||
|     free(a->buffer); | ||||
|     a->buffer = NULL; | ||||
| 
 | ||||
|     free(a->word); | ||||
|     a->word = NULL; | ||||
| 
 | ||||
|     free(a->completed); | ||||
|     a->completed = NULL; | ||||
| 
 | ||||
|     w_strfreev(a->completions); | ||||
|     a->completions = NULL; | ||||
|     a->completions_size = 0; | ||||
| 
 | ||||
|     a->buffer_size = a->search_pos = a->pos = a->word_pos = a->start = a->end = 0; | ||||
| } | ||||
| 
 | ||||
| bool ncdc_autocomplete_prepare(ncdc_autocomplete_t a, | ||||
|                                wchar_t const *s, ssize_t sz, | ||||
|                                size_t pos) | ||||
| { | ||||
|     return_if_true(a == NULL || s == NULL, false); | ||||
| 
 | ||||
|     size_t start = pos, end = pos; | ||||
|     ssize_t i = 0, words = 0; | ||||
|     bool inword = false; | ||||
| 
 | ||||
|     return_if_true(a->buffer != NULL && a->word != NULL, true); | ||||
| 
 | ||||
|     if (sz < 0) { | ||||
|         sz = wcslen(s); | ||||
|     } | ||||
| 
 | ||||
|     /* figure out the current word for autocompletion
 | ||||
|      */ | ||||
|     for (start = pos; !iswspace(s[start]) && start > 0; start--) | ||||
|         ; | ||||
| 
 | ||||
|     for (end = pos; !iswspace(s[end]) && pos < sz; end++) | ||||
|         ; | ||||
| 
 | ||||
|     /* count words backwards, to figure out what position our
 | ||||
|      * current word has in the string. this is important for | ||||
|      * others because they might have positional arguments | ||||
|      */ | ||||
|     if (start > 0) { | ||||
|         for (i = start; i >= 0; i--) { | ||||
|             if (iswspace(s[i])) { | ||||
|             inword = false; | ||||
|             } else if (!inword) { | ||||
|                 inword = true; | ||||
|                 ++words; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     free(a->buffer); | ||||
|     a->buffer = wcsndup(s, sz); | ||||
|     a->buffer_size = sz; | ||||
| 
 | ||||
|     a->start = start; | ||||
|     a->end = end; | ||||
| 
 | ||||
|     a->pos = pos; | ||||
|     a->word_pos = words; | ||||
| 
 | ||||
|     a->word = wcsndup(s + start, end); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void ncdc_autocomplete_completions(ncdc_autocomplete_t a, | ||||
|                                    wchar_t **words, ssize_t num) | ||||
| { | ||||
|     return_if_true(a == NULL,); | ||||
| 
 | ||||
|     w_strfreev(a->completions); | ||||
|     a->completions = NULL; | ||||
|     a->completions_size = 0; | ||||
| 
 | ||||
|     if (words != NULL) { | ||||
|         a->completions = w_strdupv(words, num); | ||||
|         a->completions_size = w_strlenv(words); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int ncdc_autocomplete_word_index(ncdc_autocomplete_t a) | ||||
| { | ||||
|     return_if_true(a == NULL, -1); | ||||
|     return a->word_pos; | ||||
| } | ||||
| 
 | ||||
| bool ncdc_autocomplete_complete(ncdc_autocomplete_t a, int *newpos) | ||||
| { | ||||
|     bool found = false; | ||||
|     size_t wordlen = 0, foundlen = 0; | ||||
|     wchar_t const *item = NULL; | ||||
| 
 | ||||
|     return_if_true(a == NULL, false); | ||||
| 
 | ||||
|     wordlen = wcslen(a->word); | ||||
| 
 | ||||
|     for (; a->search_pos < a->completions_size; a->search_pos++) { | ||||
|         item = a->completions[a->search_pos]; | ||||
|         if (wcsncmp(a->word, item, wordlen) == 0) { | ||||
|             found = true; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ++a->search_pos; | ||||
| 
 | ||||
|     if (a->search_pos >= a->completions_size) { | ||||
|         a->search_pos = 0; /* wrap around search */ | ||||
|     } | ||||
| 
 | ||||
|     if (!found) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     free(a->completed); | ||||
|     a->completed = NULL; | ||||
| 
 | ||||
|     foundlen = wcslen(item); | ||||
| 
 | ||||
|     a->completed = calloc(wcslen(a->buffer) + foundlen + 1, sizeof(wchar_t)); | ||||
|     return_if_true(a->completed == NULL, false); | ||||
| 
 | ||||
|     wcsncat(a->completed, a->buffer, a->start); | ||||
|     wcscat(a->completed, item); | ||||
|     wcscat(a->completed, a->buffer + a->end); | ||||
| 
 | ||||
|     if (newpos != NULL) { | ||||
|         *newpos = a->start + foundlen; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| wchar_t const *ncdc_autocomplete_completed(ncdc_autocomplete_t a) | ||||
| { | ||||
|     return_if_true(a == NULL, NULL); | ||||
|     return a->completed; | ||||
| } | ||||
| @ -44,6 +44,9 @@ static pthread_t thr; | ||||
| static pthread_mutex_t mtx; | ||||
| static pthread_cond_t cnd; | ||||
| 
 | ||||
| static wchar_t **names; | ||||
| static size_t names_size; | ||||
| 
 | ||||
| typedef struct { | ||||
|     ncdc_commands_t *cmd; | ||||
|     wchar_t *f; | ||||
| @ -93,6 +96,8 @@ bool ncdc_dispatch_init(void) | ||||
| { | ||||
|     return_if_true(queue != NULL, true); | ||||
| 
 | ||||
|     size_t i = 0; | ||||
| 
 | ||||
|     queue = g_queue_new(); | ||||
|     return_if_true(queue == NULL, false); | ||||
| 
 | ||||
| @ -101,6 +106,16 @@ bool ncdc_dispatch_init(void) | ||||
| 
 | ||||
|     pthread_create(&thr, NULL, async_dispatcher, NULL); | ||||
| 
 | ||||
|     /* build a table of names for the autocompletion, note that
 | ||||
|      * the cmds array is already one size too large (to hold a NULL) | ||||
|      */ | ||||
|     names_size = (sizeof(cmds) / sizeof(ncdc_commands_t)) - 1; | ||||
|     names = calloc(names_size + 1, sizeof(wchar_t*)); | ||||
| 
 | ||||
|     for (i = 0; cmds[i].name != NULL; i++) { | ||||
|         names[i] = wcsdup(cmds[i].name); | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| @ -121,6 +136,10 @@ bool ncdc_dispatch_deinit(void) | ||||
|     g_queue_free(queue); | ||||
|     queue = NULL; | ||||
| 
 | ||||
|     w_strfreev(names); | ||||
|     names = NULL; | ||||
|     names_size = 0; | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| @ -137,6 +156,16 @@ ncdc_commands_t *ncdc_find_cmd(ncdc_commands_t *cmds, wchar_t const *name) | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| wchar_t **ncdc_cmd_names(void) | ||||
| { | ||||
|     return names; | ||||
| } | ||||
| 
 | ||||
| size_t ncdc_cmd_names_size(void) | ||||
| { | ||||
|     return names_size; | ||||
| } | ||||
| 
 | ||||
| bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s) | ||||
| { | ||||
|     wchar_t **tokens = NULL; | ||||
|  | ||||
| @ -19,6 +19,7 @@ | ||||
| #include <ncdc/input.h> | ||||
| #include <ncdc/ncdc.h> | ||||
| #include <ncdc/keycodes.h> | ||||
| #include <ncdc/cmds.h> | ||||
| 
 | ||||
| struct ncdc_input_ | ||||
| { | ||||
| @ -30,6 +31,8 @@ struct ncdc_input_ | ||||
| 
 | ||||
|     ncdc_input_callback_t callback; | ||||
|     void *callback_arg; | ||||
| 
 | ||||
|     ncdc_autocomplete_t autocomplete; | ||||
| }; | ||||
| 
 | ||||
| static void ncdc_input_enter(ncdc_input_t p); | ||||
| @ -38,6 +41,8 @@ static void ncdc_input_free(ncdc_input_t p) | ||||
| { | ||||
|     g_array_unref(p->buffer); | ||||
| 
 | ||||
|     dc_unref(p->autocomplete); | ||||
| 
 | ||||
|     free(p); | ||||
| } | ||||
| 
 | ||||
| @ -53,9 +58,60 @@ ncdc_input_t ncdc_input_new(void) | ||||
| 
 | ||||
|     p->keys = keys_emacs; | ||||
| 
 | ||||
|     p->autocomplete = ncdc_autocomplete_new(); | ||||
| 
 | ||||
|     return p; | ||||
| } | ||||
| 
 | ||||
| static void ncdc_input_autocomplete(ncdc_input_t i) | ||||
| { | ||||
|     bool complete = false; | ||||
|     bool ret = false; | ||||
|     wchar_t const *completed = NULL; | ||||
|     int newpos = 0; | ||||
| 
 | ||||
|     ret = ncdc_autocomplete_prepare(i->autocomplete, | ||||
|                                     (wchar_t const*)i->buffer->data, -1, | ||||
|                                     i->cursor | ||||
|         ); | ||||
|     if (!ret) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (ncdc_autocomplete_word_index(i->autocomplete) == 0) { | ||||
|         /* check if we have a command, and if so, autocomplete for
 | ||||
|          * commands. | ||||
|          */ | ||||
|         if (g_array_index(i->buffer, wchar_t, 0) == L'/') { | ||||
|             ncdc_autocomplete_completions(i->autocomplete, | ||||
|                                           ncdc_cmd_names(), | ||||
|                                           ncdc_cmd_names_size() | ||||
|                 ); | ||||
|             complete = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (complete) { | ||||
|         ret = ncdc_autocomplete_complete(i->autocomplete, &newpos); | ||||
|         if (ret) { | ||||
|             completed = ncdc_autocomplete_completed(i->autocomplete); | ||||
|             /* exchange our buffer, for the autocompleted buffer
 | ||||
|              */ | ||||
|             g_array_remove_range(i->buffer, 0, i->buffer->len); | ||||
|             g_array_append_vals(i->buffer, completed, wcslen(completed)); | ||||
|             i->cursor = newpos; | ||||
| 
 | ||||
|             if (i->cursor >= i->buffer->len) { | ||||
|                 wchar_t space = L' '; | ||||
|                 g_array_append_val(i->buffer, space); | ||||
|                 ++i->cursor; | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         ncdc_autocomplete_reset(i->autocomplete); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ncdc_input_feed(ncdc_input_t input, wchar_t const *c, size_t sz) | ||||
| { | ||||
|     return_if_true(input == NULL,); | ||||
| @ -63,11 +119,16 @@ void ncdc_input_feed(ncdc_input_t input, wchar_t const *c, size_t sz) | ||||
| 
 | ||||
|     if (c[0] == '\r') { | ||||
|         ncdc_input_enter(input); | ||||
|         ncdc_autocomplete_reset(input->autocomplete); | ||||
|     } else if (c[0] == '\t') { | ||||
|         ncdc_input_autocomplete(input); | ||||
|     } else if ((bind = ncdc_find_keybinding(input->keys, c, sz)) != NULL) { | ||||
|         bind->handler(input); | ||||
|         ncdc_autocomplete_reset(input->autocomplete); | ||||
|     } else if (iswprint(c[0])) { | ||||
|         g_array_insert_vals(input->buffer, input->cursor, &c[0], 1); | ||||
|         input->cursor += wcswidth(&c[0], 1); | ||||
|         ncdc_autocomplete_reset(input->autocomplete); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -113,6 +113,29 @@ void w_strfreev(wchar_t **s) | ||||
|     free(s); | ||||
| } | ||||
| 
 | ||||
| wchar_t **w_strdupv(wchar_t **s, ssize_t sz) | ||||
| { | ||||
|     wchar_t **copy = NULL; | ||||
|     size_t i = 0; | ||||
| 
 | ||||
|     if (sz < 0) { | ||||
|         sz = w_strlenv(s); | ||||
|     } | ||||
| 
 | ||||
|     copy = calloc(sz + 1, sizeof(wchar_t*)); | ||||
|     return_if_true(copy == NULL, NULL); | ||||
| 
 | ||||
|     for (i = 0; i < sz; i++) { | ||||
|         copy[i] = wcsdup(s[i]); | ||||
|         if (copy[i] == NULL) { | ||||
|             w_strfreev(copy); | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return copy; | ||||
| } | ||||
| 
 | ||||
| wchar_t **w_tokenise(wchar_t const *str) | ||||
| { | ||||
|     wchar_t const *p = NULL, *start_of_word = NULL; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user