word completion



Hi Pavel!

> > > What do you think about `word completion' within the
 editor? 
> >
> > I'm affraid that all those cool features won't appear in MC unless you
> > implement it and do it very cleanly, documenting every change.
> 
> I will think about it, really ;-)
> (Maybe when I've finished my studies!? (in a few months))

I couldn't wait anymore :-))), so I've implemented it the last two days 
(rather nights ;-).

Here is a brief description:
If you press <ALT-TAB> in the editor window and the cursor is at the end
or inside of a word it is tried to complete this word. Thereby something
is a word that starts with a-z, A-Z, or _ and consists of a-z, A-Z, _,
or 0-9. Everything else is ignored. It is searched for completions doing
a regular expression search backwards starting before the word to be
completed up to the beginning of the file, i.e. words behind the current
cursor position are ignored. The regular expression consists of the word
to be completed followed by the string "[a-zA-Z_0-9]+". It is searched
for whole words only (case sensitive). If there is only a single match
it is used to complete the word. If there are several matches a
selection dialog is poped up allowing the user to choose its preferred
completion. 

I know, usually there is at first a beep and only if you press <ALT-TAB>
again the dialog is poped up. But I couldn't implement it this way
because I can't find out whether it is the first or second time
<ALT-TAB> is pressed. Therefore the WEdit structure needs to be extended
(a "int first;" would be enough as in WInput), and I don't want to make
changes to it without asking someone. By the way, at the moment the
limit of the entries in the selection dialog is 100. Too much? Too less?

I don't really know if this is a good implementation. It works fine and
I've tried to implement it the same way the rest of MC is implemented.
But, try it out, and if you like it and you decide to add it to the
official MC I would be very very proud :-) There are absolutely no
copyrights.

Best regards,
Matthias

PS: Best wishes for 2002 for you, Steef and all the others!
diff -Naur edit_old/edit.c edit/edit.c
--- edit_old/edit.c	Sat Dec 22 01:32:20 2001
+++ edit/edit.c	Sun Dec 30 01:53:09 2001
@@ -2485,6 +2485,9 @@
     case CK_Replace_Again:
 	edit_replace_cmd (edit, 1);
 	break;
+    case CK_Complete_Word:
+	edit_complete_word_cmd (edit);
+	break;
 
     case CK_Exit:
 	edit_quit_cmd (edit);
@@ -2598,6 +2601,7 @@
     case CK_Find_Again:
     case CK_Replace:
     case CK_Replace_Again:
+    case CK_Complete_Word:
 	edit->prev_col = edit_get_col (edit);
 	return 1;
 	break;
diff -Naur edit_old/edit.h edit/edit.h
--- edit_old/edit.h	Mon Nov  5 10:23:05 2001
+++ edit/edit.h	Sun Dec 30 01:54:51 2001
@@ -264,6 +264,7 @@
 void edit_quit_cmd (WEdit * edit);
 void edit_replace_cmd (WEdit * edit, int again);
 void edit_search_cmd (WEdit * edit, int again);
+void edit_complete_word_cmd (WEdit * edit);
 int edit_save_block (WEdit * edit, const char *filename, long start, long finish);
 int edit_save_block_cmd (WEdit * edit);
 int edit_insert_file_cmd (WEdit * edit);
diff -Naur edit_old/edit_key_translator.c edit/edit_key_translator.c
--- edit_old/edit_key_translator.c	Mon Nov 19 23:52:22 2001
+++ edit/edit_key_translator.c	Sun Dec 30 01:55:46 2001
@@ -46,7 +46,7 @@
      XCTRL ('d'), CK_Delete, '\n', CK_Enter,
      KEY_PPAGE, CK_Page_Up, KEY_NPAGE, CK_Page_Down, KEY_LEFT, CK_Left,
      KEY_RIGHT, CK_Right, KEY_UP, CK_Up, KEY_DOWN, CK_Down,
-     ALT ('\t'), CK_Return, ALT ('\n'), CK_Return,
+     ALT ('\t'), CK_Complete_Word, ALT ('\n'), CK_Return,
      KEY_HOME, CK_Home, KEY_END, CK_End, '\t', CK_Tab,
      XCTRL ('u'), CK_Undo, KEY_IC, CK_Toggle_Insert,
      XCTRL ('o'), CK_Shell, KEY_F (3), CK_Mark,
@@ -74,7 +74,7 @@
     {OUR_BACKSPACE_KEY, CK_BackSpace, OUR_DELETE_KEY, CK_Delete, '\n', CK_Enter,
      KEY_PPAGE, CK_Page_Up, KEY_NPAGE, CK_Page_Down, KEY_LEFT, CK_Left,
      KEY_RIGHT, CK_Right, KEY_UP, CK_Up, KEY_DOWN, CK_Down,
-     ALT ('\t'), CK_Return, ALT ('\n'), CK_Return,
+     ALT ('\t'), CK_Complete_Word, ALT ('\n'), CK_Return,
      KEY_HOME, CK_Home, KEY_END, CK_End, '\t', CK_Tab,
      XCTRL ('u'), CK_Undo, KEY_IC, CK_Toggle_Insert,
      XCTRL ('o'), CK_Shell, KEY_F (3), CK_Mark, KEY_F (13), CK_Column_Mark,
diff -Naur edit_old/editcmd.c edit/editcmd.c
--- edit_old/editcmd.c	Wed Dec 26 20:17:51 2001
+++ edit/editcmd.c	Sun Dec 30 01:59:41 2001
@@ -2382,3 +2382,262 @@
 	pipe_mail (edit, mail_to_last, mail_subject_last, mail_cc_last);
     }
 }
+
+
+/*******************/
+/* Word Completion */
+/*******************/
+
+
+/* find first character of current word */
+static int edit_find_word_start (WEdit *edit, long *word_start, int *word_len)
+{
+    int i, c, last;
+    
+/* return if at begin of file */
+    if (edit->curs1 <= 0)
+	return 0;
+
+    c = (unsigned char) edit_get_byte (edit, edit->curs1 - 1);
+/* return if not at end or in word */
+    if (isspace (c) || !(isalnum (c) || c == '_'))
+	return 0; 
+
+/* search start of word to be completed */
+    for (i = 2;; i++) {
+/* return if at begin of file */
+	if (edit->curs1 - i < 0) 
+	    return 0;
+	    
+	last = c;
+	c = (unsigned char) edit_get_byte (edit, edit->curs1 - i);
+
+	if (!(isalnum (c) || c == '_')) {
+/* return if word starts with digit */
+	    if (isdigit (last))
+		return 0;
+
+	    *word_start = edit->curs1 - (i - 1); /* start found */
+	    *word_len = i - 1;
+	    break;
+	}
+    }
+/* success */
+    return 1;
+}
+
+
+/* (re)set search parameters to the given values */
+static void edit_set_search_parameters (int rs, int rb, int rr, int rw, int rc)
+{
+    replace_scanf = rs;
+    replace_backwards = rb;
+    replace_regexp = rr;
+    replace_whole = rw;
+    replace_case = rc;
+}
+
+
+unsigned int MAX_WORD_COMPLETIONS = 100; /* in listbox */
+unsigned int compl_dlg_h; /* completion dialog height */
+unsigned int compl_dlg_w; /* completion dialog width */
+
+
+/* collect the possible completions */ 
+static int edit_collect_completions (WEdit *edit, long start, 
+    int word_len, char *match_expr, struct selection *compl, int *num)
+{
+    int len, max_len = 0, i, skip;
+    char *bufpos;
+    
+/* collect max MAX_WORD_COMPLETIONS completions */
+    while (*num < MAX_WORD_COMPLETIONS) {
+/* get next match */
+	start = edit_find (start - 1, (unsigned char *) match_expr, &len, 
+	    edit->last_byte, (int (*)(void *, long)) edit_get_byte, 
+	    (void *) edit, 0);
+
+/* not matched */
+	if (start < 0)
+	    break;
+	
+/* add matched completion if not yet added */
+	bufpos = &edit->buffers1[start >> S_EDIT_BUF_SIZE][start & M_EDIT_BUF_SIZE];
+	skip = 0;
+	for (i = 0; i < *num; i++) {
+	    if (strncmp (&compl[i].text[word_len], &bufpos[word_len], 
+		max (len, compl[i].len) - word_len) == 0) {
+		skip = 1;
+		break; /* skip it, already added */
+	    }
+	}
+	if (skip)
+	    continue;
+
+	compl[*num].text = CMalloc (len + 1);
+	compl[*num].len = len;
+	for (i = 0; i < len; i++)
+	    compl[*num].text[i] = *(bufpos + i);
+	compl[*num].text[i] = '\0';
+	(*num)++;
+	
+/* note the maximal length needed for the completion dialog */
+	if (len > max_len)
+	    max_len = len;
+    }
+    return max_len; 
+}
+
+
+/* completion dialog callback */
+static int compl_callback (Dlg_head *h, int key, int Msg)
+{
+    switch (Msg) {
+    case DLG_DRAW:
+	attrset (COLOR_NORMAL);
+	dlg_erase (h);
+	draw_box (h, 0, 0, compl_dlg_h, compl_dlg_w);
+	break;
+    }
+    return 0;
+}
+
+
+static int compllist_callback (void *data)
+{
+    return 0;
+}
+
+
+/* let the user select its preferred completion */
+void edit_completion_dialog (WEdit *edit, int max_len, int word_len, 
+    struct selection *compl, int num_compl)
+{
+    int start_x, start_y, offset, i;
+    char *curr = NULL;
+    Dlg_head *compl_dlg;
+    WListbox *compl_list;
+    	    
+/* calculate the dialog metrics */
+    start_x = edit->curs_col - ((max_len + 4) / 2);
+    start_y = edit->curs_row + 2;
+	
+    compl_dlg_h = num_compl + 2;
+    compl_dlg_w = max_len + 4;
+
+    if (start_x <= 0) 
+        start_x = 0;
+
+    if (compl_dlg_w >= COLS) 
+        compl_dlg_w = COLS;
+
+    if (start_x + compl_dlg_w >= COLS) {
+        offset = start_x + compl_dlg_w - COLS;
+        start_x -= offset;
+        if (start_x <= 0) 
+    	    start_x = 0;
+    }
+	    
+    if (start_y + compl_dlg_h >= LINES) {
+        offset = start_y + compl_dlg_h - LINES;
+        start_y -= offset;
+        if (start_y <= 0) 
+    	    start_y = 1;
+	if (start_y + compl_dlg_h >= LINES)
+	    compl_dlg_h = LINES - 2;
+    }
+
+/* create the dialog */    
+    compl_dlg = create_dlg (start_y, start_x, compl_dlg_h, compl_dlg_w,
+	dialog_colors, compl_callback, "[Word Completion]", "complete_word", 
+	DLG_NONE);
+	    
+/* create the listbox */
+    compl_list = listbox_new (1, 1, compl_dlg_w - 2, compl_dlg_h - 2, 0, 
+	compllist_callback, NULL);
+	
+/* add the dialog */
+    add_widget (compl_dlg, compl_list);
+
+/* fill the listbox with the completions */
+    for (i = 0; i < num_compl; i++)
+        listbox_add_item (compl_list, 0, 0, compl[i].text, NULL);
+    	    
+/* pop up the dialog */
+    run_dlg (compl_dlg);
+
+/* apply the choosen completion */
+    if (compl_dlg->ret_value == B_ENTER) {
+    	listbox_get_current (compl_list, &curr, NULL);
+	if (curr)
+	    for (curr += word_len; *curr; curr++)
+		edit_insert (edit, *curr);
+    }
+
+/* destroy dialog before return */
+    destroy_dlg (compl_dlg);
+}
+
+
+/* complete current word using regular expression search */
+/* backwards beginning at current cursor position        */
+void edit_complete_word_cmd (WEdit *edit)
+{
+    int word_len = 0, i, num_compl = 0, max_len;
+    long word_start = 0;
+    char *bufpos;
+    char match_expr[MAX_REPL_LEN];
+    struct selection compl[MAX_WORD_COMPLETIONS]; /* completions */
+    
+/* don't want to disturb another search */
+    int old_rs = replace_scanf;
+    int old_rb = replace_backwards;
+    int old_rr = replace_regexp;
+    int old_rw = replace_whole;
+    int old_rc = replace_case;
+
+/* search start of word to be completed */
+    if (!edit_find_word_start (edit, &word_start, &word_len))
+	return;
+
+/* prepare match expression */
+    bufpos = &edit->buffers1[word_start >> S_EDIT_BUF_SIZE]
+			    [word_start & M_EDIT_BUF_SIZE];
+    strncpy (match_expr, bufpos, word_len);
+    match_expr[word_len] = '\0';
+    strcat (match_expr, "[a-zA-Z_0-9]+");
+    
+/* init search: backward, regexp, whole word, case sensitive */
+    edit_set_search_parameters (0, 1, 1, 1, 1);
+
+/* collect the possible completions              */
+/* start search from curs1 down to begin of file */
+    max_len = edit_collect_completions (edit, word_start, word_len, 
+	match_expr, (struct selection *) &compl, &num_compl);
+
+    if (num_compl > 0) {
+/* insert completed word if there is only one match */
+	if (num_compl == 1) {
+	    for (i = word_len; i < compl[0].len; i++)
+		edit_insert (edit, *(compl[0].text + i));
+	} 
+/* more than one possible completion => ask the user */
+	else {
+/* !!! usually only a beep is expected and when <ALT-TAB> is !!! */
+/* !!! pressed again the selection dialog pops up, but that  !!! */
+/* !!! seems to require a further internal state	     !!! */
+	    /*beep ();*/
+    
+/* let the user select the preferred completion */
+	    edit_completion_dialog (edit, max_len, word_len, 
+		(struct selection *) &compl, num_compl);
+	}
+    }
+    
+/* release memory before return */
+    for (i = 0; i < num_compl; i++)
+        free (compl[i].text);
+
+/* restore search parameters */
+    edit_set_search_parameters (old_rs, old_rb, old_rr, old_rw, old_rc);
+}
diff -Naur edit_old/editcmddef.h edit/editcmddef.h
--- edit_old/editcmddef.h	Fri Sep  7 22:10:45 2001
+++ edit/editcmddef.h	Sun Dec 30 02:00:19 2001
@@ -54,6 +54,7 @@
 #define CK_Find_Again		302
 #define CK_Replace		303
 #define CK_Replace_Again	304
+#define CK_Complete_Word	305
 
 /* debugger commands */
 #define CK_Debug_Start		350


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]