From 36f568543d9fad9e097a5a9e05d474fc3ed1cd0f Mon Sep 17 00:00:00 2001 From: Sumedh Pendurkar Date: Wed, 19 Oct 2016 15:37:15 +0200 Subject: [PATCH] new feature: complete a fragment to a longer word found in the buffer This function is bound by default to the ^] keystroke. --- src/global.c | 8 ++++ src/nano.c | 7 ++- src/nano.h | 5 ++ src/proto.h | 3 ++ src/text.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 1 deletion(-) diff --git a/src/global.c b/src/global.c index babdbc9..02f7361 100644 --- a/src/global.c +++ b/src/global.c @@ -59,6 +59,9 @@ int last_line_y; message_type lastmessage = HUSH; /* Messages of type HUSH should not overwrite type MILD nor ALERT. */ +filestruct *pletion_line = NULL; + /* The line where the last completion was found, if any. */ + int controlleft, controlright, controlup, controldown; #ifndef NANO_TINY int shiftcontrolleft, shiftcontrolright, shiftcontrolup, shiftcontroldown; @@ -536,6 +539,7 @@ void shortcut_init(void) #endif const char *nano_undo_msg = N_("Undo the last 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 const char *nano_back_msg = N_("Go back one character"); const char *nano_forward_msg = N_("Go forward one character"); @@ -813,6 +817,9 @@ void shortcut_init(void) N_("Undo"), IFSCHELP(nano_undo_msg), TOGETHER, NOVIEW); add_to_funcs(do_redo, MMAIN, 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 */ add_to_funcs(do_left, MMAIN, @@ -1095,6 +1102,7 @@ void shortcut_init(void) add_to_sclist(MMAIN, "M-{", 0, do_unindent, 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, "^]", 0, complete_a_word, 0); #endif #ifdef ENABLE_COMMENT add_to_sclist(MMAIN, "M-3", 0, do_comment, 0); diff --git a/src/nano.c b/src/nano.c index a060592..8b52136 100644 --- a/src/nano.c +++ b/src/nano.c @@ -1684,7 +1684,9 @@ int do_input(bool allow_funcs) } } - if (have_shortcut) { + if (!have_shortcut) + pletion_line = NULL; + else { const subnfunc *f = sctofunc(s); if (ISSET(VIEW_MODE) && f && !f->viewok) { @@ -1702,6 +1704,9 @@ int do_input(bool allow_funcs) preserve = TRUE; #ifndef NANO_TINY + if (s->scfunc != complete_a_word) + pletion_line = NULL; + if (s->scfunc == do_toggle_void) { do_toggle(s->toggle); if (s->toggle != CUT_TO_END) diff --git a/src/nano.h b/src/nano.h index 93ec5e1..195562d 100644 --- a/src/nano.h +++ b/src/nano.h @@ -484,6 +484,11 @@ typedef struct subnfunc { /* Next item in the list. */ } subnfunc; +typedef struct pletion_word { + char *word; + struct pletion_word *next; +} pletion_word; + /* The elements of the interface that can be colored differently. */ enum { diff --git a/src/proto.h b/src/proto.h index 8bb16da..3386374 100644 --- a/src/proto.h +++ b/src/proto.h @@ -47,6 +47,8 @@ extern int last_line_y; extern message_type lastmessage; +extern filestruct *pletion_line; + extern int controlleft; extern int controlright; extern int controlup; @@ -664,6 +666,7 @@ void do_formatter(void); void do_wordlinechar_count(void); #endif void do_verbatim_input(void); +void complete_a_word(void); /* All functions in utils.c. */ void get_homedir(void); diff --git a/src/text.c b/src/text.c index ca77709..c1db222 100644 --- a/src/text.c +++ b/src/text.c @@ -48,6 +48,13 @@ static filestruct *jusbottom = NULL; /* A pointer to the end of the buffer with unjustified text. */ #endif +static int pletion_x = 0; + /* The x position in pletion_line of the last found completion. */ +static int pleted_length = 0; + /* The number of bytes added in the last completion. */ +static pletion_word *pletion_word_list; + /* A linked list of the words that are possible completions. */ + #ifndef NANO_TINY /* Toggle the mark. */ void do_mark(void) @@ -3684,3 +3691,148 @@ void do_verbatim_input(void) free(output); } + +/* Copy the found possible completion word. */ +char *copy_completion(char *check_line, int start) +{ + char *word; + int i = start, j = 0; + int len_of_word = 0; + + while (is_word_mbchar(&check_line[i++], FALSE)) + len_of_word++; + + word = (char *)nmalloc((len_of_word + 1) * sizeof(char)); + + i = start; + + while (is_word_mbchar(&check_line[i], FALSE)) + word[j++] = check_line[i++]; + + word[j] = '\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 fill in the next + * possible completion. */ +void complete_a_word(void) +{ + char *shard, *completion = NULL; + int to_find_start_pos, shard_length = 0; + int i = 0, j = 0; + pletion_word *word_current; + + /* If this is a fresh completion attempt, clear the list of words of a + * possible previous completion run. */ + if (pletion_line == NULL) { + while (pletion_word_list != NULL) { + pletion_word *dropit = pletion_word_list; + pletion_word_list = pletion_word_list->next; + 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 earlier completion from the buffer. */ + for (i = 0; i < pleted_length; i++) + do_backspace(); + + /* Pop the last two undo items off the stack, because the completion + * suggestion and its deletion should not be remembered. */ + discard_until(openfile->current_undo->next->next, openfile); + openfile->current_undo = openfile->undotop; + } + + to_find_start_pos = openfile->current_x; + word_current = pletion_word_list; + + /* Find the start of the fragment that the user typed. */ + while (--to_find_start_pos >= 0) + if (!is_word_mbchar(&openfile->current->data[to_find_start_pos], FALSE)) + break; + to_find_start_pos++; + + /* If there is no word fragment before the cursor, do nothing. */ + if (to_find_start_pos == openfile->current_x) { + pletion_line = NULL; + return; + } + + shard = (char *)nmalloc((openfile->current_x - to_find_start_pos + 1) * sizeof(char)); + + /* Copy the fragment that has to be searched for. */ + while (to_find_start_pos < openfile->current_x) + shard[shard_length++] = openfile->current->data[to_find_start_pos++]; + shard[shard_length] = '\0'; + + /* Search the fragment in the file. */ + while (pletion_line != NULL) { + int line_length = strlen(pletion_line->data); + + for (i = pletion_x; i + shard_length < line_length; i++) { + /* Ignore the fragment itself. */ + if (pletion_line == openfile->current && i == openfile->current_x - shard_length) + continue; + + for (j = 0; (i == 0 || !is_word_mbchar(&pletion_line->data[i - 1], FALSE)) && + j < shard_length; j++) + if (shard[j] != pletion_line->data[i + j]) + break; + + if (j == shard_length && (i + j < line_length && + is_word_mbchar(&pletion_line->data[i + j], FALSE))) { + completion = copy_completion(pletion_line->data, i); + + /* Skip this word if we've already seen it. */ + while (word_current) { + if (strcmp(completion, word_current->word) == 0) + break; + word_current = word_current->next; + } + if (word_current) { + i++; + continue; + } + + /* Add the word to the list of completions. */ + word_current = (pletion_word *)nmalloc(sizeof(pletion_word)); + word_current->word = completion; + word_current->next = pletion_word_list; + pletion_word_list = word_current; + + /* Inject the completion into the buffer. */ + pleted_length = strlen(completion) - shard_length; + do_output(&word_current->word[shard_length], pleted_length, FALSE); + pletion_x = ++i; + + free(shard); + return; + } + } + + pletion_line = pletion_line->next; + pletion_x = 0; + } + + if (pletion_line == NULL) { + if (pletion_word_list != NULL) { + statusline(HUSH, "No further matches"); + refresh_needed = TRUE; + } else + statusline(ALERT, "No matches"); + } + + free(completion); + free(shard); +} -- 2.9.3