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
parent
86121cf3fc
commit
dca4ab5d8f
10
src/global.c
10
src/global.c
|
@ -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);
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
181
src/text.c
181
src/text.c
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue