Refactor completion handling and add FSF Readline callbacks

Still a bit raw, but the basic building blocks are there, waiting to be
cleaned up and released.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson 2018-09-10 18:34:27 +02:00
parent b2f1cfbbcc
commit 77d531f5b5
3 changed files with 210 additions and 5 deletions

View File

@ -44,7 +44,6 @@ typedef enum {
} el_status_t;
/* Editline specific types, despite rl_ prefix. From Heimdal project. */
typedef char* rl_complete_func_t(char*, int*);
typedef int rl_list_possib_func_t(char*, char***);
typedef el_status_t el_keymap_func_t(void);
typedef int rl_hook_func_t(void);
@ -53,6 +52,11 @@ typedef void rl_voidfunc_t(void);
typedef void rl_vintfunc_t(int);
typedef void rl_vcpfunc_t(char *);
/* FSF Readline compat tupes */
typedef char *rl_complete_func_t (char *, int*);
typedef char *rl_compentry_func_t (const char *, int);
typedef char **rl_completion_func_t (const char *, int, int);
/* Display 8-bit chars "as-is" or as `M-x'? Toggle with M-m. (Default:0 - "as-is") */
extern int rl_meta_chars;
@ -70,6 +74,8 @@ extern const char *el_prev_hist(void);
extern char *rl_complete(char *token, int *match);
extern int rl_list_possib(char *token, char ***av);
extern char **rl_completion_matches(const char *token, rl_compentry_func_t *generator);
extern char *rl_filename_completion_function(const char *text, int state);
/* For compatibility with FSF readline. */
extern int rl_point;
@ -108,9 +114,14 @@ extern void add_history (const char *line);
extern int read_history (const char *filename);
extern int write_history (const char *filename);
//extern int rl_complete2 (int ignore, int invoking_key);
extern rl_completion_func_t *rl_attempted_completion_function;
extern rl_complete_func_t *rl_set_complete_func (rl_complete_func_t *func);
extern rl_list_possib_func_t *rl_set_list_possib_func (rl_list_possib_func_t *func);
//#define rl_complete(a, b) _Generic((a), int: rl_complete2, default: rl_complete)(a, b)
/* Alternate interface to plain readline(), for event loops */
extern void rl_callback_handler_install (const char *prompt, rl_vcpfunc_t *lhandler);
extern void rl_callback_read_char (void);

View File

@ -20,10 +20,15 @@
* 4. This notice may not be removed or altered.
*/
#include <ctype.h>
#include "editline.h"
#define MAX_TOTAL_MATCHES (256 << sizeof(char *))
int rl_attempted_completion_over = 0;
rl_completion_func_t *rl_attempted_completion_function = NULL;
rl_compentry_func_t *rl_completion_entry_function = NULL;
/* Wrap strcmp() for qsort() -- weird construct to pass -Wcast-qual */
static int compare(const void *p1, const void *p2)
{
@ -121,7 +126,7 @@ static int FindMatches(char *dir, char *file, char ***avp)
}
/* Split a pathname into allocated directory and trailing filename parts. */
static int SplitPath(char *path, char **dirpart, char **filepart)
static int SplitPath(const char *path, char **dirpart, char **filepart)
{
static char DOT[] = ".";
char *dpart;
@ -177,7 +182,7 @@ char *el_filename_complete(char *pathname, int *match)
size_t j;
size_t len;
if (SplitPath(pathname, &dir, &file) < 0)
if (SplitPath((const char *)pathname, &dir, &file) < 0)
return NULL;
if ((ac = FindMatches(dir, file, &av)) == 0) {
@ -236,11 +241,202 @@ char *el_filename_complete(char *pathname, int *match)
return p;
}
char *rl_filename_completion_function(const char *text, int state)
{
char *dir;
char *file;
static char **av;
static size_t i, ac;
if (!state) {
if (SplitPath(text, &dir, &file) < 0)
return NULL;
ac = FindMatches(dir, file, &av);
free(dir);
free(file);
if (!ac)
return NULL;
i = 0;
}
if (i < ac)
return av[i++];
do {
free(av[--i]);
} while (i > 0);
return NULL;
}
/* Similar to el_find_word(), but used by GNU Readline API */
static char *rl_find_token(size_t *len)
{
char *ptr;
int pos;
for (pos = rl_point; pos < rl_end; pos++) {
if (isspace(rl_line_buffer[pos])) {
if (pos > 0)
pos--;
break;
}
}
ptr = &rl_line_buffer[pos];
while (pos >= 0 && !isspace(rl_line_buffer[pos])) {
if (pos == 0)
break;
pos--;
}
if (ptr != &rl_line_buffer[pos]) {
*len = (size_t)(ptr - &rl_line_buffer[pos]);
return &rl_line_buffer[pos];
}
return NULL;
}
/*
* "uses an application-supplied generator function to generate the list
* of possible matches, and then returns the array of these matches. The
* caller should place the address of its generator function in
* rl_completion_entry_function"
*/
char **rl_completion_matches(const char *token, rl_compentry_func_t *generator)
{
int state = 0, num = 0;
char **array, *entry;
if (!generator) {
generator = rl_completion_entry_function;
if (!generator)
generator = rl_filename_completion_function;
}
if (!generator)
return NULL;
array = malloc(512 * sizeof(char *));
if (!array)
return NULL;
while (num < 511 && (entry = generator(token, state))) {
state = 1;
array[num++] = entry;
}
array[num] = NULL;
if (!num) {
free(array);
return NULL;
}
return array;
}
#if 0 // incomplete + incorrect atm
/*
* Implements the actual FSF readline rl_complete() API
*
* "isolates the word to be completed and calls rl_completion_matches()
* to generate a list of possible completions"
*/
int rl_complete2(int ignore, int invoking_key)
{
char *token, *word, **words = NULL;
size_t len;
token = rl_find_token(&len);
if (!token)
return 0; /* Nothing inserted */
word = strndup(token, len);
rl_attempted_completion_over = 0;
if (rl_attempted_completion_function) {
int start = token - rl_line_buffer;
int end = start + len;
words = rl_attempted_completion_function(word, start, end);
}
if (!rl_attempted_completion_over && !words)
words = rl_completion_matches(word, NULL);
if (words && words[0]) {
int i = 1;
free(word);
word = words[0];
while (words[i])
free(words[i++]);
free(words);
return word;
}
return el_filename_complete(word, match);
}
#endif
static char *complete(char *token, int *match)
{
size_t len = 0;
char *word, **words = NULL;
int start, end;
word = rl_find_token(&len);
if (!word)
goto fallback;
start = word - rl_line_buffer;
end = start + len;
word = strndup(word, len);
if (!word)
goto fallback;
rl_attempted_completion_over = 0;
words = rl_attempted_completion_function(word, start, end);
if (!rl_attempted_completion_over && !words)
words = rl_completion_matches(word, NULL);
if (words && words[0]) {
int i = 0;
free(word);
word = strdup(words[0] + len);
while (words[i])
free(words[i++]);
free(words);
return word;
}
fallback:
return el_filename_complete(token, match);
}
/*
* First check for editline specific custom completion function, then
* for any GNU Readline compat, then fallback to filename completion.
*/
char *rl_complete(char *token, int *match)
{
if (el_complete_func)
return el_complete_func(token, match);
if (rl_attempted_completion_function)
return complete(token, match);
return el_filename_complete(token, match);
}

View File

@ -67,8 +67,6 @@ rl_hook_func_t *rl_event_hook;
rl_vintfunc_t *rl_prep_term_function = rl_prep_terminal;
rl_voidfunc_t *rl_deprep_term_function = rl_deprep_terminal;
char **(*rl_attempted_completion_function)(const char *token, int start, int end);
/*
** Globals.
*/