new feature: complete a fragment to a longer word found in the buffer

Executing the 'complete_a_word' function will search from the start
of the current buffer for entire words that begin with the fragment
that is before the cursor, and will complete this fragment to the
first word that is found.  Each consecutive call of 'complete_a_word'
will search for the next matching word and will complete the fragment
to that.  By default the function is bound to the ^] keystroke.

Signed-off-by: Sumedh Pendurkar <sumedh.pendurkar@gmail.com>
Signed-off-by: Benno Schulenberg <bensberg@justemail.net>
master
Sumedh Pendurkar 2016-12-07 09:43:47 +05:30 committed by Benno Schulenberg
parent 86121cf3fc
commit dca4ab5d8f
5 changed files with 209 additions and 1 deletions

View File

@ -59,6 +59,11 @@ int last_line_y;
message_type lastmessage = HUSH; message_type lastmessage = HUSH;
/* Messages of type HUSH should not overwrite type MILD nor ALERT. */ /* Messages of type HUSH should not overwrite type MILD nor ALERT. */
#ifndef NANO_TINY
filestruct *pletion_line = NULL;
/* The line where the last completion was found, if any. */
#endif
int controlleft, controlright, controlup, controldown; int controlleft, controlright, controlup, controldown;
#ifndef NANO_TINY #ifndef NANO_TINY
int shiftcontrolleft, shiftcontrolright, shiftcontrolup, shiftcontroldown; int shiftcontrolleft, shiftcontrolright, shiftcontrolup, shiftcontroldown;
@ -536,6 +541,7 @@ void shortcut_init(void)
#endif #endif
const char *nano_undo_msg = N_("Undo the last operation"); const char *nano_undo_msg = N_("Undo the last operation");
const char *nano_redo_msg = N_("Redo the last undone operation"); const char *nano_redo_msg = N_("Redo the last undone operation");
const char *nano_completion_msg = N_("Try and complete the current word");
#endif #endif
const char *nano_back_msg = N_("Go back one character"); const char *nano_back_msg = N_("Go back one character");
const char *nano_forward_msg = N_("Go forward one character"); const char *nano_forward_msg = N_("Go forward one character");
@ -813,6 +819,9 @@ void shortcut_init(void)
N_("Undo"), IFSCHELP(nano_undo_msg), TOGETHER, NOVIEW); N_("Undo"), IFSCHELP(nano_undo_msg), TOGETHER, NOVIEW);
add_to_funcs(do_redo, MMAIN, add_to_funcs(do_redo, MMAIN,
N_("Redo"), IFSCHELP(nano_redo_msg), BLANKAFTER, NOVIEW); N_("Redo"), IFSCHELP(nano_redo_msg), BLANKAFTER, NOVIEW);
add_to_funcs(complete_a_word, MMAIN,
N_("Complete"), IFSCHELP(nano_completion_msg), BLANKAFTER, NOVIEW);
#endif /* !NANO_TINY */ #endif /* !NANO_TINY */
add_to_funcs(do_left, MMAIN, add_to_funcs(do_left, MMAIN,
@ -1095,6 +1104,7 @@ void shortcut_init(void)
add_to_sclist(MMAIN, "M-{", 0, do_unindent, 0); add_to_sclist(MMAIN, "M-{", 0, do_unindent, 0);
add_to_sclist(MMAIN, "M-U", 0, do_undo, 0); add_to_sclist(MMAIN, "M-U", 0, do_undo, 0);
add_to_sclist(MMAIN, "M-E", 0, do_redo, 0); add_to_sclist(MMAIN, "M-E", 0, do_redo, 0);
add_to_sclist(MMAIN, "^]", 0, complete_a_word, 0);
#endif #endif
#ifdef ENABLE_COMMENT #ifdef ENABLE_COMMENT
add_to_sclist(MMAIN, "M-3", 0, do_comment, 0); add_to_sclist(MMAIN, "M-3", 0, do_comment, 0);

View File

@ -1672,6 +1672,9 @@ int do_input(bool allow_funcs)
preserve = TRUE; preserve = TRUE;
#ifndef NANO_TINY #ifndef NANO_TINY
if (s->scfunc != complete_a_word)
pletion_line = NULL;
if (s->scfunc == do_toggle_void) { if (s->scfunc == do_toggle_void) {
do_toggle(s->toggle); do_toggle(s->toggle);
if (s->toggle != CUT_TO_END) if (s->toggle != CUT_TO_END)
@ -1707,6 +1710,10 @@ int do_input(bool allow_funcs)
update_line(openfile->current, openfile->current_x); update_line(openfile->current, openfile->current_x);
} }
} }
#ifndef NANO_TINY
else
pletion_line = NULL;
#endif
/* If we aren't cutting or copying text, and the key wasn't a toggle, /* If we aren't cutting or copying text, and the key wasn't a toggle,
* blow away the text in the cutbuffer upon the next cutting action. */ * blow away the text in the cutbuffer upon the next cutting action. */

View File

@ -484,6 +484,13 @@ typedef struct subnfunc {
/* Next item in the list. */ /* Next item in the list. */
} subnfunc; } subnfunc;
#ifndef NANO_TINY
typedef struct completion_word {
char *word;
struct completion_word *next;
} completion_word;
#endif
/* The elements of the interface that can be colored differently. */ /* The elements of the interface that can be colored differently. */
enum enum
{ {

View File

@ -47,6 +47,10 @@ extern int last_line_y;
extern message_type lastmessage; extern message_type lastmessage;
#ifndef NANO_TINY
extern filestruct *pletion_line;
#endif
extern int controlleft; extern int controlleft;
extern int controlright; extern int controlright;
extern int controlup; extern int controlup;
@ -645,6 +649,7 @@ void do_formatter(void);
void do_wordlinechar_count(void); void do_wordlinechar_count(void);
#endif #endif
void do_verbatim_input(void); void do_verbatim_input(void);
void complete_a_word(void);
/* All functions in utils.c. */ /* All functions in utils.c. */
void get_homedir(void); void get_homedir(void);

View File

@ -48,6 +48,13 @@ static filestruct *jusbottom = NULL;
/* A pointer to the end of the buffer with unjustified text. */ /* A pointer to the end of the buffer with unjustified text. */
#endif #endif
#ifndef NANO_TINY
static int pletion_x = 0;
/* The x position in pletion_line of the last found completion. */
static completion_word *list_of_completions;
/* A linked list of the completions that have been attepmted. */
#endif
#ifndef NANO_TINY #ifndef NANO_TINY
/* Toggle the mark. */ /* Toggle the mark. */
void do_mark(void) void do_mark(void)
@ -812,7 +819,7 @@ void do_undo(void)
break; break;
} }
if (undidmsg) if (undidmsg && !pletion_line)
statusline(HUSH, _("Undid action (%s)"), undidmsg); statusline(HUSH, _("Undid action (%s)"), undidmsg);
renumber(f); renumber(f);
@ -3676,3 +3683,175 @@ void do_verbatim_input(void)
free(output); free(output);
} }
#ifndef NANO_TINY
/* Copy the found completion candidate. */
char *copy_completion(char *check_line, int start)
{
char *word;
int position = start, len_of_word = 0, index = 0;
/* Find the length of the word by travelling to its end. */
while (is_word_mbchar(&check_line[position], FALSE)) {
int next = move_mbright(check_line, position);
len_of_word += next - position;
position = next;
}
word = (char *)nmalloc((len_of_word + 1) * sizeof(char));
/* Simply copy the word. */
while (index < len_of_word)
word[index++] = check_line[start++];
word[index] = '\0';
return word;
}
/* Look at the fragment the user has typed, then search the current buffer for
* the first word that starts with this fragment, and tentatively complete the
* fragment. If the user types 'Complete' again, search and paste the next
* possible completion. */
void complete_a_word(void)
{
char *shard, *completion = NULL;
int start_of_shard, shard_length = 0;
int i = 0, j = 0;
completion_word *some_word;
bool was_set_wrapping = !ISSET(NO_WRAP);
/* If this is a fresh completion attempt... */
if (pletion_line == NULL) {
/* Clear the list of words of a previous completion run. */
while (list_of_completions != NULL) {
completion_word *dropit = list_of_completions;
list_of_completions = list_of_completions->next;
free(dropit->word);
free(dropit);
}
/* Prevent a completion from being merged with typed text. */
openfile->last_action = OTHER;
/* Initialize the starting point for searching. */
pletion_line = openfile->fileage;
pletion_x = 0;
/* Wipe the "No further matches" message. */
blank_statusbar();
wnoutrefresh(bottomwin);
} else {
/* Remove the attempted completion from the buffer. */
do_undo();
}
/* Find the start of the fragment that the user typed. */
start_of_shard = openfile->current_x;
while (start_of_shard > 0) {
int step_left = move_mbleft(openfile->current->data, start_of_shard);
if (!is_word_mbchar(&openfile->current->data[step_left], FALSE))
break;
start_of_shard = step_left;
}
/* If there is no word fragment before the cursor, do nothing. */
if (start_of_shard == openfile->current_x) {
pletion_line = NULL;
return;
}
shard = (char *)nmalloc((openfile->current_x - start_of_shard + 1) * sizeof(char));
/* Copy the fragment that has to be searched for. */
while (start_of_shard < openfile->current_x)
shard[shard_length++] = openfile->current->data[start_of_shard++];
shard[shard_length] = '\0';
/* Run through all of the lines in the buffer, looking for shard. */
while (pletion_line != NULL) {
int threshold = strlen(pletion_line->data) - shard_length - 1;
/* The point where we can stop searching for shard. */
/* Traverse the whole line, looking for shard. */
for (i = pletion_x; i < threshold; i++) {
/* If the first byte doesn't match, run on. */
if (pletion_line->data[i] != shard[0])
continue;
/* Compare the rest of the bytes in shard. */
for (j = 1; j < shard_length; j++)
if (pletion_line->data[i + j] != shard[j])
break;
/* If not all of the bytes matched, continue searching. */
if (j < shard_length)
continue;
/* If the found match is not /longer/ than shard, skip it. */
if (!is_word_mbchar(&pletion_line->data[i + j], FALSE))
continue;
/* If the match is not a separate word, skip it. */
if (i > 0 && is_word_mbchar(&pletion_line->data[
move_mbleft(pletion_line->data, i)], FALSE))
continue;
/* If this match is the shard itself, ignore it. */
if (pletion_line == openfile->current &&
i == openfile->current_x - shard_length)
continue;
completion = copy_completion(pletion_line->data, i);
/* Look among earlier attempted completions for a duplicate. */
some_word = list_of_completions;
while (some_word && strcmp(some_word->word, completion) != 0)
some_word = some_word->next;
/* If we've already tried this word, skip it. */
if (some_word != NULL) {
free(completion);
continue;
}
/* Add the found word to the list of completions. */
some_word = (completion_word *)nmalloc(sizeof(completion_word));
some_word->word = completion;
some_word->next = list_of_completions;
list_of_completions = some_word;
/* Temporarily disable wrapping so only one undo item is added. */
SET(NO_WRAP);
/* Inject the completion into the buffer. */
do_output(&completion[shard_length],
strlen(completion) - shard_length, FALSE);
/* If needed, reenable wrapping and wrap the current line. */
if (was_set_wrapping) {
UNSET(NO_WRAP);
do_wrap(openfile->current);
}
/* Set the position for a possible next search attempt. */
pletion_x = ++i;
free(shard);
return;
}
pletion_line = pletion_line->next;
pletion_x = 0;
}
/* The search has reached the end of the file. */
if (list_of_completions != NULL) {
statusline(ALERT, _("No further matches"));
refresh_needed = TRUE;
} else
statusline(ALERT, _("No matches"));
free(shard);
}
#endif