mirror of
https://github.com/troglobit/editline.git
synced 2025-05-06 04:21:24 +08:00
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:
parent
b2f1cfbbcc
commit
77d531f5b5
@ -44,7 +44,6 @@ typedef enum {
|
|||||||
} el_status_t;
|
} el_status_t;
|
||||||
|
|
||||||
/* Editline specific types, despite rl_ prefix. From Heimdal project. */
|
/* 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 int rl_list_possib_func_t(char*, char***);
|
||||||
typedef el_status_t el_keymap_func_t(void);
|
typedef el_status_t el_keymap_func_t(void);
|
||||||
typedef int rl_hook_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_vintfunc_t(int);
|
||||||
typedef void rl_vcpfunc_t(char *);
|
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") */
|
/* Display 8-bit chars "as-is" or as `M-x'? Toggle with M-m. (Default:0 - "as-is") */
|
||||||
extern int rl_meta_chars;
|
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 char *rl_complete(char *token, int *match);
|
||||||
extern int rl_list_possib(char *token, char ***av);
|
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. */
|
/* For compatibility with FSF readline. */
|
||||||
extern int rl_point;
|
extern int rl_point;
|
||||||
@ -108,9 +114,14 @@ extern void add_history (const char *line);
|
|||||||
extern int read_history (const char *filename);
|
extern int read_history (const char *filename);
|
||||||
extern int write_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_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);
|
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 */
|
/* 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_handler_install (const char *prompt, rl_vcpfunc_t *lhandler);
|
||||||
extern void rl_callback_read_char (void);
|
extern void rl_callback_read_char (void);
|
||||||
|
200
src/complete.c
200
src/complete.c
@ -20,10 +20,15 @@
|
|||||||
* 4. This notice may not be removed or altered.
|
* 4. This notice may not be removed or altered.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include "editline.h"
|
#include "editline.h"
|
||||||
|
|
||||||
#define MAX_TOTAL_MATCHES (256 << sizeof(char *))
|
#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 */
|
/* Wrap strcmp() for qsort() -- weird construct to pass -Wcast-qual */
|
||||||
static int compare(const void *p1, const void *p2)
|
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. */
|
/* 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[] = ".";
|
static char DOT[] = ".";
|
||||||
char *dpart;
|
char *dpart;
|
||||||
@ -177,7 +182,7 @@ char *el_filename_complete(char *pathname, int *match)
|
|||||||
size_t j;
|
size_t j;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
if (SplitPath(pathname, &dir, &file) < 0)
|
if (SplitPath((const char *)pathname, &dir, &file) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if ((ac = FindMatches(dir, file, &av)) == 0) {
|
if ((ac = FindMatches(dir, file, &av)) == 0) {
|
||||||
@ -236,11 +241,202 @@ char *el_filename_complete(char *pathname, int *match)
|
|||||||
return p;
|
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)
|
char *rl_complete(char *token, int *match)
|
||||||
{
|
{
|
||||||
if (el_complete_func)
|
if (el_complete_func)
|
||||||
return el_complete_func(token, match);
|
return el_complete_func(token, match);
|
||||||
|
|
||||||
|
if (rl_attempted_completion_function)
|
||||||
|
return complete(token, match);
|
||||||
|
|
||||||
return el_filename_complete(token, match);
|
return el_filename_complete(token, match);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +67,6 @@ rl_hook_func_t *rl_event_hook;
|
|||||||
rl_vintfunc_t *rl_prep_term_function = rl_prep_terminal;
|
rl_vintfunc_t *rl_prep_term_function = rl_prep_terminal;
|
||||||
rl_voidfunc_t *rl_deprep_term_function = rl_deprep_terminal;
|
rl_voidfunc_t *rl_deprep_term_function = rl_deprep_terminal;
|
||||||
|
|
||||||
char **(*rl_attempted_completion_function)(const char *token, int start, int end);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Globals.
|
** Globals.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user