/* automatic word completion - called when space/enter is pressed */ char *auto_word_complete(const char *line, int *pos) { GString *result; const char *replace; char *word, *wordstart, *ret; int startpos; g_return_val_if_fail(line != NULL, NULL); g_return_val_if_fail(pos != NULL, NULL); word = get_word_at(line, *pos, &wordstart); startpos = (int) (wordstart-line); result = g_string_new(line); g_string_erase(result, startpos, strlen(word)); /* check for words in autocompletion list */ replace = wordreplace_find(word); if (replace == NULL) { ret = NULL; g_string_free(result, TRUE); } else { *pos = startpos+strlen(replace); g_string_insert(result, startpos, replace); ret = result->str; g_string_free(result, FALSE); } g_free(word); return ret; }
/* manual word completion - called when TAB is pressed */ char *word_complete(WINDOW_REC *window, const char *line, int *pos) { static int startpos = 0, wordlen = 0; GString *result; char *word, *wordstart, *linestart, *ret; int want_space; g_return_val_if_fail(line != NULL, NULL); g_return_val_if_fail(pos != NULL, NULL); if (complist != NULL && *pos == last_line_pos && strcmp(line, last_line) == 0) { /* complete from old list */ complist = complist->next != NULL ? complist->next : g_list_first(complist); want_space = last_want_space; } else { /* get new completion list */ free_completions(); /* get the word we want to complete */ word = get_word_at(line, *pos, &wordstart); startpos = (int) (wordstart-line); wordlen = strlen(word); /* get the start of line until the word we're completing */ if (isseparator(*line)) { /* empty space at the start of line */ if (wordstart == line) wordstart += strlen(wordstart); } else { while (wordstart > line && isseparator(wordstart[-1])) wordstart--; } linestart = g_strndup(line, (int) (wordstart-line)); /* completions usually add space after the word, that makes things a bit harder. When continuing a completion "/msg nick1 "<tab> we have to cycle to nick2, etc. BUT if we start completion with "/msg "<tab>, we don't want to complete the /msg word, but instead complete empty word with /msg being in linestart. */ if (*pos > 0 && line[*pos-1] == ' ') { char *old; old = linestart; linestart = *linestart == '\0' ? g_strdup(word) : g_strconcat(linestart, " ", word, NULL); g_free(old); g_free(word); word = g_strdup(""); startpos = strlen(linestart)+1; wordlen = 0; } want_space = TRUE; signal_emit("complete word", 5, &complist, window, word, linestart, &want_space); last_want_space = want_space; g_free(linestart); g_free(word); } if (complist == NULL) return NULL; /* word completed */ *pos = startpos+strlen(complist->data); /* replace the word in line - we need to return a full new line */ result = g_string_new(line); g_string_erase(result, startpos, wordlen); g_string_insert(result, startpos, complist->data); if (want_space) { if (!isseparator(result->str[*pos])) g_string_insert_c(result, *pos, ' '); (*pos)++; } wordlen = strlen(complist->data); last_line_pos = *pos; g_free_not_null(last_line); last_line = g_strdup(result->str); ret = result->str; g_string_free(result, FALSE); return ret; }
/* manual word completion - called when TAB is pressed */ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, int backward) { static int startpos = 0, wordlen = 0; int old_startpos, old_wordlen; GString *result; const char *cmdchars; char *word, *wordstart, *linestart, *ret, *data; int continue_complete, want_space, expand_escapes; g_return_val_if_fail(line != NULL, NULL); g_return_val_if_fail(pos != NULL, NULL); continue_complete = complist != NULL && *pos == last_line_pos && g_strcmp0(line, last_line) == 0; if (erase && !continue_complete) return NULL; old_startpos = startpos; old_wordlen = wordlen; if (!erase && continue_complete) { word = NULL; linestart = NULL; } else { char* old_wordstart; /* get the word we want to complete */ word = get_word_at(line, *pos, &wordstart); old_wordstart = wordstart; startpos = (int) (wordstart-line); wordlen = strlen(word); /* remove trailing spaces from linestart */ while (wordstart > line && isseparator_space(wordstart[-1])) wordstart--; /* unless everything was spaces */ if (old_wordstart > line && wordstart == line) wordstart = old_wordstart - 1; linestart = g_strndup(line, (int) (wordstart-line)); /* completions usually add space after the word, that makes things a bit harder. When continuing a completion "/msg nick1 "<tab> we have to cycle to nick2, etc. BUT if we start completion with "/msg "<tab>, we don't want to complete the /msg word, but instead complete empty word with /msg being in linestart. */ if (!erase && *pos > 0 && isseparator_space(line[*pos-1]) && (*linestart == '\0' || !isseparator_space(wordstart[-1]))) { char *old; old = linestart; /* we want to move word into linestart */ if (*linestart == '\0') { linestart = g_strdup(word); } else { GString *str = g_string_new(linestart); if (old_wordstart[-1] != str->str[str->len - 1]) { /* do not accidentally duplicate the word separator */ g_string_append_c(str, old_wordstart[-1]); } g_string_append(str, word); linestart = g_string_free(str, FALSE); } g_free(old); g_free(word); word = g_strdup(""); startpos = *linestart == '\0' ? 0 : strlen(linestart)+1; wordlen = 0; } } if (erase) { signal_emit("complete erase", 3, window, word, linestart); /* jump to next completion */ startpos = old_startpos; wordlen = old_wordlen; } if (continue_complete) { /* complete from old list */ if (backward) complist = complist->prev != NULL ? complist->prev : g_list_last(complist); else complist = complist->next != NULL ? complist->next : g_list_first(complist); want_space = last_want_space; } else { int keep_word = settings_get_bool("completion_keep_word"); /* get new completion list */ free_completions(); want_space = TRUE; signal_emit("complete word", 5, &complist, window, word, linestart, &want_space); last_want_space = want_space; if (complist != NULL) { /* Remove all nulls (from the signal) before doing further processing */ complist = g_list_remove_all(g_list_first(complist), NULL); if (keep_word) { complist = g_list_append(complist, g_strdup(word)); } if (backward) { complist = g_list_last(complist); if (keep_word) { complist = complist->prev; } } } } g_free(linestart); g_free(word); if (complist == NULL) return NULL; /* get the cmd char */ cmdchars = settings_get_str("cmdchars"); /* get the expand_escapes setting */ expand_escapes = settings_get_bool("expand_escapes"); /* escape if the word doesn't begin with '/' and expand_escapes are turned on */ data = strchr(cmdchars, *line) == NULL && expand_escapes ? escape_string_backslashes(complist->data) : g_strdup(complist->data); /* word completed */ *pos = startpos + strlen(data); /* replace the word in line - we need to return a full new line */ result = g_string_new(line); g_string_erase(result, startpos, wordlen); g_string_insert(result, startpos, data); if (want_space) { if (!isseparator(result->str[*pos])) g_string_insert_c(result, *pos, ' '); (*pos)++; } wordlen = strlen(data); last_line_pos = *pos; g_free_not_null(last_line); last_line = g_strdup(result->str); ret = result->str; g_string_free(result, FALSE); /* free the data */ g_free(data); return ret; }