editable minibuffer in mg

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

editable minibuffer in mg

Kjell-5
Let me preface this with the following: I _know_ this is a grotesque
hack. It is the result of 8 hours on a plane a zaurus and two horrible
in-flight movies.

Still, the result is much better than the status quo, and the
alternative of rewriting the minibuffer to be an ACTUAL buffer is an
exercise left for the reader.

So, here it is, in all it's unpretty glory:

This diff makes some of the more common editing keybindings work
in the minibuffer.

left, right, ^B, ^F, ^Y, ^K, ^A, ^E

It also makes completions work as you would expect them to
i.e. a *Completions* buffer pops up with your choices in it
when you hit a second TAB or SP.

If you have any other editing favorite keys, let me know.
If you want to clean any of this up, feel free.
If I've done anything stupid, yell at me.
Otherwise, please give it a spin and see if it does what you think it
should.

-kj

Index: echo.c
===================================================================
RCS file: /cvs/src/usr.bin/mg/echo.c,v
retrieving revision 1.37
diff -u -r1.37 echo.c
--- echo.c 11 Oct 2005 00:46:46 -0000 1.37
+++ echo.c 11 Nov 2005 00:45:36 -0000
@@ -18,9 +18,10 @@
 #include "funmap.h"
 
 #include <stdarg.h>
+#include <term.h>
 
 static char *veread(const char *, char *, size_t, int, va_list);
-static int complt(int, int, char *, size_t, int);
+static int complt(int, int, char *, size_t, int, int *);
 static int complt_list(int, int, char *, int);
 static void eformat(const char *, va_list);
 static void eputi(int, int);
@@ -141,8 +142,19 @@
 static char *
 veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap)
 {
- int cpos, dynbuf = (buf == NULL);
- int c, i;
+ int dynbuf = (buf == NULL);
+ int cpos, epos; /* cursor, end position in buf */
+ int c, i, y;
+ int cplflag = FALSE; /* display completion list */
+ int cwin = FALSE; /* completion list created */
+ int mr = 0; /* match left arrow */
+ int ml = 0; /* match right arrow */
+ int esc = 0; /* position in esc pattern */
+ BUFFER *bp; /* completion list buffer */
+ MGWIN *wp; /* window for compl list */
+ int match; /* esc match found */
+ int cc, rr; /* saved ttcol, ttrow */
+ char *ret; /* return value */
 
 #ifndef NO_MACRO
  if (inmacro) {
@@ -157,7 +169,10 @@
  return (buf);
  }
 #endif /* !NO_MACRO */
- cpos = 0;
+ epos = cpos = 0;
+ ml = mr = esc = 0;
+ cplflag = FALSE;
+
  if ((flag & EFNEW) != 0 || ttrow != nrow - 1) {
  ttcolor(CTEXT);
  ttmove(nrow - 1, 0);
@@ -169,38 +184,156 @@
  if (buf == NULL)
  return (NULL);
  eputs(buf);
- cpos += strlen(buf);
+ epos = cpos += strlen(buf);
  }
  tteeol();
  ttflush();
  for (;;) {
  c = getkey(FALSE);
  if ((flag & EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) {
- cpos += complt(flag, c, buf, nbuf, cpos);
+ if (cplflag == TRUE) {
+ complt_list(flag, c, buf, cpos);
+ cwin = TRUE;
+ } else if (complt(flag, c, buf, nbuf, epos, &i) == TRUE) {
+ cplflag = TRUE;
+ epos += i;
+ cpos = epos;
+ }
  continue;
  }
- if ((flag & EFAUTO) != 0 && c == '?') {
- complt_list(flag, c, buf, cpos);
- continue;
+ cplflag = FALSE;
+
+ if (esc > 0) { /* ESC sequence started */
+ match = 0;
+ if (ml == esc && key_left[ml] && c == key_left[ml]) {
+ match++;
+ if (key_left[++ml] == '\0') {
+ c = CCHR('B');
+ esc = 0;
+ }
+ }
+ if (mr == esc && key_right[mr] && c == key_right[mr]) {
+ match++;
+ if (key_right[++mr] == '\0') {
+ c = CCHR('F');
+ esc = 0;
+ }
+ }
+ if (match == 0) {
+ esc = 0;
+ continue;
+ /* hack. how do we know esc pattern is done? */
+ }
+ if (esc > 0) {
+ esc++;
+ continue;
+ }
  }
- switch (c) {
+ switch (c) {
+ case CCHR('A'): /* start of line */
+ while (cpos > 0) {
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ --ttcol;
+ }
+ ttputc('\b');
+ --ttcol;
+ }
+ ttflush();
+ break;
+ case CCHR('D'):
+ if (cpos != epos) {
+ tteeol();
+ y = buf[cpos];
+ epos--;
+ rr = ttrow;
+ cc = ttcol;
+ for (i = cpos; i < epos; i++) {
+ buf[i] = buf[i + 1];
+ eputc(buf[i]);
+ }
+ ttmove(rr, cc);
+ ttflush();
+ }
+ break;
+ case CCHR('E'): /* end of line */
+ while (cpos < epos) {
+ eputc(buf[cpos++]);
+ }
+ ttflush();
+ break;
+ case CCHR('B'): /* back */
+ if (cpos > 0) {
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ --ttcol;
+ }
+ ttputc('\b');
+ --ttcol;
+ ttflush();
+ }
+ break;
+ case CCHR('F'): /* forw */
+ if (cpos < epos) {
+ eputc(buf[cpos++]);
+ ttflush();
+ }
+ break;
+ case CCHR('Y'): /* yank from kill buffer */
+ i = 0;
+ while ((y = kremove(i++)) >= 0 && y != '\n') {
+ int t;
+ if (dynbuf && epos + 1 >= nbuf) {
+ void *newp;
+ size_t newsize = epos + epos + 16;
+ if ((newp = realloc(buf, newsize))
+    == NULL)
+ goto fail;
+ buf = newp;
+ nbuf = newsize;
+ }
+ for (t = epos; t > cpos; t--)
+ buf[t] = buf[t - 1];
+ buf[cpos++] = (char)y;
+ epos++;
+ eputc((char)y);
+ cc = ttcol;
+ rr = ttrow;
+ for (t = cpos; t < epos; t++)
+ eputc(buf[t]);
+ ttmove(rr, cc);
+ }
+ ttflush();
+ break;
+ case CCHR('K'): /* copy here-EOL to kill buffer */
+ kdelete();
+ for (i = cpos; i < epos; i++)
+ kinsert(buf[i], KFORW);
+ tteeol();
+ epos = cpos;
+ ttflush();
+ break;
+ case CCHR('['):
+ ml = mr = esc = 1;
+ break;
  case CCHR('J'):
  c = CCHR('M');
  /* FALLTHROUGH */
  case CCHR('M'): /* return, done */
  /* if there's nothing in the minibuffer, abort */
- if (cpos == 0 && !(flag & EFNUL)) {
+ if (epos == 0 && !(flag & EFNUL)) {
  (void)ctrlg(FFRAND, 0);
  ttflush();
  return (NULL);
  }
  if ((flag & EFFUNC) != 0) {
- if ((i = complt(flag, c, buf, nbuf, cpos)) == 0)
+ if (complt(flag, c, buf, nbuf, epos, &i)
+    == FALSE)
  continue;
  if (i > 0)
- cpos += i;
+ epos += i;
  }
- buf[cpos] = '\0';
+ buf[epos] = '\0';
  if ((flag & EFCR) != 0) {
  ttputc(CCHR('M'));
  ttflush();
@@ -223,25 +356,38 @@
  bcopy(buf, lp->l_text, cpos);
  }
 #endif /* !NO_MACRO */
+ ret = buf;
  goto done;
  case CCHR('G'): /* bell, abort */
  eputc(CCHR('G'));
  (void)ctrlg(FFRAND, 0);
  ttflush();
- return (NULL);
+ ret = NULL;
+ goto done;
  case CCHR('H'): /* rubout, erase */
  case CCHR('?'):
  if (cpos != 0) {
+ y = buf[--cpos];
+ epos--;
  ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttcol--;
+ if (ISCTRL(y) != FALSE) {
  ttputc('\b');
+ ttcol--;
+ }
+ rr = ttrow;
+ cc = ttcol;
+ for (i = cpos; i < epos; i++) {
+ buf[i] = buf[i + 1];
+ eputc(buf[i]);
+ }
+ ttputc(' ');
+ if (ISCTRL(y) != FALSE) {
  ttputc(' ');
  ttputc('\b');
- --ttcol;
  }
+ ttputc('\b');
+ ttmove(rr, cc);
  ttflush();
  }
  break;
@@ -258,6 +404,7 @@
  ttputc('\b');
  --ttcol;
  }
+ epos--;
  }
  ttflush();
  break;
@@ -293,35 +440,53 @@
  c = getkey(FALSE);
  /* FALLTHROUGH */
  default:
- /* all the rest */
- if (dynbuf && cpos + 1 >= nbuf) {
+ if (dynbuf && epos + 1 >= nbuf) {
  void *newp;
- size_t newsize = cpos + cpos + 16;
-
- if ((newp = realloc(buf, newsize)) == NULL) {
- ewprintf("Out of memory");
- free(buf);
- return (NULL);
- }
+ size_t newsize = epos + epos + 16;
+ if ((newp = realloc(buf, newsize)) == NULL)
+ goto fail;
  buf = newp;
  nbuf = newsize;
  }
- if (cpos < nbuf - 1) {
- buf[cpos++] = (char)c;
- eputc((char)c);
- ttflush();
- }
+ for (i = epos; i > cpos; i--)
+ buf[i] = buf[i - 1];
+ buf[cpos++] = (char)c;
+ epos++;
+ eputc((char)c);
+ cc = ttcol;
+ rr = ttrow;
+ for (i = cpos; i < epos; i++)
+ eputc(buf[i]);
+ ttmove(rr, cc);
+ ttflush();
  }
  }
+ ret = buf;
 done:
- return (buf);
+ if (cwin == TRUE) {
+ /* blow away cpltion window */
+ bp = bfind("*Completions*", TRUE);
+ if ((wp = popbuf(bp)) != NULL) {
+ curwp = wp;
+ delwind(FFRAND, 1);
+ }
+ }
+ return (ret);
+fail:
+ ewprintf("Out of memory");
+ free(buf);
+ return (NULL);
 }
 
 /*
  * Do completion on a list of objects.
+ * c is SPACE, TAB, or CR
+ * return TRUE if matched (or partially matched)
+ * FALSE is result is ambiguous,
+ * ABORT on error.
  */
 static int
-complt(int flags, int c, char *buf, size_t nbuf, int cpos)
+complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
 {
  LIST *lh, *lh2;
  LIST *wholelist = NULL;
@@ -357,7 +522,7 @@
  lh2 = lh;
  ++nhits;
  if (lh->l_name[cpos] == '\0')
- nxtra = -1;
+ nxtra = -1; /* exact match */
  else {
  bxtra = getxtra(lh, lh2, cpos, wflag);
  if (bxtra < nxtra)
@@ -375,16 +540,18 @@
  * autocompleted have known types/lengths.
  */
  if (nxtra < 0 && nhits > 1 && c == ' ')
- nxtra = 1;
+ nxtra = 1; /* ??? */
  for (i = 0; i < nxtra && cpos < nbuf; ++i) {
  buf[cpos] = lh2->l_name[cpos];
  eputc(buf[cpos++]);
  }
+ /* XXX should grow nbuf */
  ttflush();
  free_file_list(wholelist);
- if (nxtra < 0 && c != CCHR('M'))
- return (0);
- return (nxtra);
+ *nx = nxtra;
+ if (nxtra < 0 && c != CCHR('M')) /* exact */
+ *nx = 0;
+ return (TRUE);
  }
 
  /*
@@ -408,7 +575,8 @@
  ttcol -= (i = nshown); /* update ttcol on BS's */
  while (i--)
  ttputc('\b'); /* update ttcol again! */
- return (0);
+ *nx = nxtra;
+ return ((nhits > 0) ? TRUE : FALSE);
 }
 
 /*
@@ -433,8 +601,8 @@
 
  ttflush();
 
- /* The results are put into a help buffer. */
- bp = bfind("*help*", TRUE);
+ /* The results are put into a completion buffer. */
+ bp = bfind("*Completions*", TRUE);
  if (bclear(bp) == FALSE)
  return (FALSE);

Reply | Threaded
Open this post in threaded view
|

Re: editable minibuffer in mg

Han Boetes
Uhwah! Cool!!!

This is a great improvement! The only other keys I use in the
mini-buffer with emacs are m-f and m-b which move forward and
backward to the next . / - or spc



# Han