/* complete.c - brings up a possible word list Copyright (C) 1996-2000 Paul Sheer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #define COMPLETION_FILE "/cooledit.completion" /* email address with spaces */ #define ALLOW_CHARS_TYPE_A "_#.@-\\/! <>()" /* email address without spaces */ #define ALLOW_CHARS_TYPE_B "_#.@-\\/!" /* latex functions */ #define ALLOW_CHARS_TYPE_C "\\" /* C functions, and #'s */ #define ALLOW_CHARS_TYPE_D "_#" #include "coolwidget.h" #include "mad.h" extern Window main_window; char *loadfile (const char *filename, long *filelen); static char *word_list = 0; static char **words; static long num_completion_words = 0; static int compare_completion (const char **a, const char **b) { return strcmp (*a, *b); } static void load_competion_file (void) { char *f, *n; long l, i; f = loadfile (n = catstrs (home_dir, EDIT_DIR COMPLETION_FILE, 0), &l); if (!f) { CErrorDialog (main_window, 20, 20, _(" Complete Word "), _(" You have not yet created a `completion' word list \n" \ " See the section COMPLETION in the man page for \n creating a personalised word list. \n" \ " The completion word list should go into the file \n %s "), n); return; } word_list = f; i = 0; for (; *f; f++) if (*f == '\n') i++; while (*f == '\n' || !*f) /* remove trailing blank lines */ *(f--) = '\0'; words = CMalloc ((i + 4) * sizeof (char *)); i = 1; f = word_list; while (*f == '\n') /* remove leading blank lines */ f++; words[0] = f; for (;; f++) { if (*f == '\n') { *f = '\0'; words[i++] = f + 1; } else if (*f == '\0') { words[i] = 0; break; } } num_completion_words = i; qsort (words, num_completion_words, sizeof (char *), (int (*)(const void *, const void *)) compare_completion); } /* w = 0 causes return of the last word. Result must not be free'd */ static char *get_current_word (CWidget * w, char *allow_chars) { static char t[1024]; int i; static char *p; if (!w) return p; p = t + 1023; *p = '\0'; for (i = 1; i < 1021; i++) { int c; c = edit_get_byte (w->editor, w->editor->curs1 - i); if (strchr (allow_chars, c) || isalnum (c)) { *(--p) = c; continue; } break; } return p; } /* result must be free'd, returns 0 on not found */ static char **get_possible_words (CWidget * w, char *allow_chars) { char *p, **r; int i, l, o; p = get_current_word (w, allow_chars); l = strlen (p); if (!l) return 0; o = i = num_completion_words / 2; for (;;) { /* binary search */ int comp; i = (i + 1) >> 1; if (o >= num_completion_words) /* is this possible ? */ o = num_completion_words - 1; if (o < 0) /* is this possible ? */ o = 0; comp = strncmp (words[o], p, l); if (!comp) break; if (comp < 0) o += i; else o -= i; if (i == 1) { if (o >= num_completion_words) o = num_completion_words - 1; if (o < 0) o = 0; if (strncmp (words[o], p, l)) return 0; } } while (o) { /* find the first word that matches */ if (strncmp (words[o - 1], p, l)) break; o--; } i = o; while (words[i]) { /* find the last word that matches */ if (strncmp (words[i], p, l)) break; i++; } i -= o; r = CMalloc ((i + 1) * sizeof (char *)); memcpy (r, words + o, i * sizeof (char *)); r[i] = 0; return r; } static char *complete_selection_get_line (void *data, int line) { char **s; s = (char **) data; if (s[line]) return s[line]; return ""; } static int get_selection_complete (char **s) { int i, c = 0; for (i = 0; s[i]; i++) { int l; l = strlen (s[i]); if (c < l) c = l; } return CListboxDialog (main_window, 20, 20, c, min (i, 10), 0, 0, 0, i, complete_selection_get_line, (void *) s); } static void complete_with_word (CWidget * w, char *s) { s += strlen (get_current_word (0, 0)); while (*s) { edit_insert (w->editor, *s++); w->editor->force |= REDRAW_COMPLETELY; } } void complete_command (CWidget * edit) { char **s; if (!edit) { if (word_list) { free (word_list); word_list = 0; } if (words) { free (words); words = 0; } return; } if (!word_list) load_competion_file (); if (!word_list) return; s = get_possible_words (edit, ALLOW_CHARS_TYPE_A); if (!s) s = get_possible_words (edit, ALLOW_CHARS_TYPE_B); if (!s) s = get_possible_words (edit, ALLOW_CHARS_TYPE_C); if (!s) s = get_possible_words (edit, ALLOW_CHARS_TYPE_D); if (!s) return; if (!s[1]) { complete_with_word (edit, s[0]); } else { int i; i = get_selection_complete (s); if (i >= 0) complete_with_word (edit, s[i]); } free (s); }