面白そうなパッチがあったので、screen 4.0.3に充てられるように修正してみた。ドキュメントファイルのパッチはめんどくさいのでパス。
diff -ur screen-4.0.3/comm.c screen-4.0.3.dabbrev/comm.c --- screen-4.0.3/comm.c 2003-09-08 23:25:08.000000000 +0900 +++ screen-4.0.3.dabbrev/comm.c 2007-04-22 21:30:35.000000000 +0900 @@ -122,6 +122,9 @@ #ifdef COPY_PASTE { "copy", NEED_FORE|ARGS_0 }, { "crlf", ARGS_01 }, +#ifdef DABBREV + { "dabbrev", NEED_FORE|ARGS_0 }, +#endif #endif { "debug", ARGS_01 }, #ifdef AUTO_NUKE diff -ur screen-4.0.3/config.h.in screen-4.0.3.dabbrev/config.h.in --- screen-4.0.3/config.h.in 2006-10-23 22:06:32.000000000 +0900 +++ screen-4.0.3.dabbrev/config.h.in 2007-04-22 21:33:14.000000000 +0900 @@ -178,6 +178,7 @@ # define COLORS16 # define ZMODEM # define BLANKER_PRG +# define DABBREV #endif /* SIMPLESCREEN */ #undef BUILTIN_TELNET diff -ur screen-4.0.3/mark.c screen-4.0.3.dabbrev/mark.c --- screen-4.0.3/mark.c 2003-09-08 23:26:00.000000000 +0900 +++ screen-4.0.3.dabbrev/mark.c 2007-04-22 21:42:11.000000000 +0900 @@ -81,6 +81,9 @@ static struct markdata *markdata; +#ifdef DABBREV +int restart_dabbrev = 1; /* used in process.c */ +#endif /* * VI like is_letter: 0 - whitespace @@ -428,6 +431,131 @@ /**********************************************************************/ +#ifdef DABBREV +/* The following code implements `dynamic abbreviation' expansion a la + Emacs. It looks in the preceding visible screen and its history to find + expansions of a typed word. It checks consecutive expansions and + ignores one of them if they are identical. (Tomasz J. Cholewo, + t.cholewo@ieee.org) */ + +int +getprevchar (xp, yp) +int *xp, *yp; +{ + static char *linep; + static int y = -1; + while (*yp >= 0) { + if (*yp != y) { /* cache last |linep| value */ + y = *yp; + linep = WIN (y)->image; + } + if (--*xp >= 0) + return linep[*xp]; + *xp = D_width; + (*yp)--; /* go to previous line */ + if (!fore->w_wrap) + return ' '; /* separate lines */ + } + return -1; +} + +char * +getprevword (xp, yp) +int *xp, *yp; +{ +#define MAXWLEN 1000 + static char ab[MAXWLEN]; /* initialized to zeros */ + char *abword; + int c; + + abword = ab + MAXWLEN - 1; + + while ((c = getprevchar (xp, yp)) >= 0 && is_letter (c)) + if (abword > ab) /* store only |MAXWLEN| last chars */ + *(--abword) = c; + if (c < 0) { + if (abword < ab + MAXWLEN - 1) + return abword; + else + return 0; + } + + while ((c = getprevchar (xp, yp)) >= 0 && !is_letter (c)); /* skip preceding spaces */ + (*xp)++; /* can be | >= D_width| */ + return abword; +} + +/* return value 1 if u_plop.buf changed */ +int +DabbrevExpand () +{ + static int x, y, hintlen; + static char *hint = 0, *lastexpansion = 0; + + char *expansion; + int del_cnt, buf_cnt, i; + + if (restart_dabbrev) { /* initialize */ + restart_dabbrev = 0; + x = fore->w_x; + if (x > D_width) + x = D_width; + y = fore->w_y + fore->w_histheight; + + free (hint); /* |free (NULL)| is OK */ + hint = getprevword (&x, &y); + if (!hint) + return 0; /* no preceding word? */ + free (lastexpansion); + lastexpansion = strdup (hint); + hint = strdup (hint); /* make our own copy */ + if (!hint || !lastexpansion) + { + Msg(0, "Not enough memory... Sorry."); + return 0; + } + hintlen = strlen (hint); + } + + while ((expansion = getprevword (&x, &y))) { + if (!strncmp (hint, expansion, hintlen) && /* empty hint matches everything */ + strlen (expansion) > hintlen && /* trivia disallowed */ + strcmp (expansion, lastexpansion)) /* different from previous */ + break; + } + if (!expansion) /* no expansion found */ + return 0; + + UserFreeCopyBuffer(D_user); + + del_cnt = strlen (lastexpansion) - hintlen; + buf_cnt = del_cnt + strlen (expansion) - hintlen; + if (!(D_user->u_plop.buf = malloc((unsigned) (buf_cnt + 1)))) + { + Msg(0, "Not enough memory... Sorry."); + return 0; + } + for (i = 0; i < del_cnt; i++) { /* delete previous expansion */ + D_user->u_plop.buf[i] /* is this the best way to find out the ERASE character? */ +#ifdef POSIX + = D_OldMode.tio.c_cc[VERASE]; +#else + = D_OldMode.m_ttyb.sg_erase; +#endif + } + bcopy (expansion + hintlen, D_user->u_plop.buf + del_cnt, strlen (expansion) - hintlen); + D_user->u_plop.len = buf_cnt; + + free (lastexpansion); + lastexpansion = strdup (expansion); + if (!lastexpansion) + { + Msg(0, "Not enough memory... Sorry."); + return 0; + } + return 1; +} +#endif /* DABBREV */ void MarkRoutine() @@ -697,20 +825,20 @@ case '|': revto(--rep_cnt, cy); break; - case 'w': + case 'w': /* word forward */ if (rep_cnt == 0) rep_cnt = 1; nextword(&cx, &cy, NW_MUSTMOVE, rep_cnt); revto(cx, cy); break; - case 'e': + case 'e': /* word forward last letter */ case 'E': if (rep_cnt == 0) rep_cnt = 1; nextword(&cx, &cy, NW_ENDOFWORD|NW_MUSTMOVE | (od == 'E' ? NW_BIG : 0), rep_cnt); revto(cx, cy); break; - case 'b': + case 'b': /* word back */ case 'B': if (rep_cnt == 0) rep_cnt = 1; diff -ur screen-4.0.3/process.c screen-4.0.3.dabbrev/process.c --- screen-4.0.3/process.c 2003-09-18 21:53:54.000000000 +0900 +++ screen-4.0.3.dabbrev/process.c 2007-04-22 21:49:59.000000000 +0900 @@ -45,6 +45,10 @@ #include "extern.h" #include "logfile.h" +#ifdef DABBREV +extern int restart_dabbrev; +#endif + extern struct comm comms[]; extern char *rc_name; extern char *RcFileName, *home; @@ -552,6 +556,9 @@ ktab['X'].nr = RC_REMOVE; ktab['F'].nr = RC_FIT; ktab['\t'].nr = RC_FOCUS; +#ifdef DABBREV + ktab['/'].nr = RC_DABBREV; +#endif /* These come last; they may want overwrite others: */ if (DefaultEsc >= 0) { @@ -806,9 +813,13 @@ ilen--; } slen -= ilen; +#ifdef DABBREV + if (slen > 0) /* normal keystroke */ + restart_dabbrev = 1; +#endif if (slen) DoProcess(fore, &ibuf, &slen, 0); - if (--ilen == 0) + if (--ilen == 0) /* ESC was last */ D_ESCseen = ktab; } if (ilen <= 0) @@ -1071,6 +1082,12 @@ #endif /* MULTIUSER */ msgok = display && !*rc_name; + +#ifdef DABBREV /* all commands assumed to disrupt */ + if (nr != RC_DABBREV) + restart_dabbrev = 1; +#endif + switch(nr) { case RC_SELECT: @@ -2087,6 +2104,10 @@ } MarkRoutine(); break; + +#ifdef DABBREV + case RC_DABBREV: /* tjc */ +#endif case RC_HISTORY: { static char *pasteargs[] = {".", 0}; @@ -2097,6 +2118,15 @@ Msg(0, "Must be on a window layer"); break; } + +#ifdef DABBREV + if (nr == RC_DABBREV) { + if (DabbrevExpand() == 0) { + AddCStr(D_BL); + break; + } + } else +#endif /* DABBREV */ if (GetHistory() == 0) break; if (user->u_plop.buf == NULL) @@ -3823,7 +3853,7 @@ break; default: #ifdef HAVE_BRAILLE - /* key == -2: input from braille keybord, msgok always 0 */ + /* key == -2: input from braille keyboard, msgok always 0 */ DoBrailleAction(act, key == -2 ? 0 : msgok); #endif break;
適当に文字を入力して「M-/」を押すと、バッファ内の単語で補完してくれる。
シェルに限らず、エディタでも何でも使えるところが面白い。
しばらく使ってみる予定。
screenて結構、私家版が多いのかも。