Switch completion movement keys

This commit is contained in:
Out Of Ideas 2023-11-13 16:16:51 -06:00
parent 00819865a9
commit 24281f7120
8 changed files with 481 additions and 12 deletions

View File

@ -0,0 +1,53 @@
diff --git a/dmenu.1 b/dmenu.1
index 323f93c..3e3b31b 100644
--- a/dmenu.1
+++ b/dmenu.1
@@ -3,7 +3,7 @@
dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
-.RB [ \-bfiv ]
+.RB [ \-bfsv ]
.RB [ \-l
.IR lines ]
.RB [ \-m
@@ -44,8 +44,8 @@ dmenu appears at the bottom of the screen.
dmenu grabs the keyboard before reading stdin if not reading from a tty. This
is faster, but will lock up X until stdin reaches end\-of\-file.
.TP
-.B \-i
-dmenu matches menu items case insensitively.
+.B \-s
+dmenu matches menu items case sensitively.
.TP
.BI \-l " lines"
dmenu lists items vertically, with the given number of lines.
diff --git a/dmenu.c b/dmenu.c
index 65f25ce..855df59 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -55,8 +55,9 @@ static Clr *scheme[SchemeLast];
#include "config.h"
-static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
-static char *(*fstrstr)(const char *, const char *) = strstr;
+static char * cistrstr(const char *s, const char *sub);
+static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
+static char *(*fstrstr)(const char *, const char *) = cistrstr;
static void
appenditem(struct item *item, struct item **list, struct item **last)
@@ -709,9 +710,9 @@ main(int argc, char *argv[])
topbar = 0;
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
fast = 1;
- else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
- fstrncmp = strncasecmp;
- fstrstr = cistrstr;
+ else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
+ fstrncmp = strncmp;
+ fstrstr = strstr;
} else if (i + 1 == argc)
usage();
/* these options take one argument */

BIN
dmenu

Binary file not shown.

View File

@ -3,7 +3,7 @@
dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-bfiv ]
.RB [ \-bfsv ]
.RB [ \-l
.IR lines ]
.RB [ \-m
@ -44,8 +44,8 @@ dmenu appears at the bottom of the screen.
dmenu grabs the keyboard before reading stdin if not reading from a tty. This
is faster, but will lock up X until stdin reaches end\-of\-file.
.TP
.B \-i
dmenu matches menu items case insensitively.
.B \-s
dmenu matches menu items case sensitively.
.TP
.BI \-l " lines"
dmenu lists items vertically, with the given number of lines.

194
dmenu.1.orig Normal file
View File

@ -0,0 +1,194 @@
.TH DMENU 1 dmenu\-VERSION
.SH NAME
dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-bfiv ]
.RB [ \-l
.IR lines ]
.RB [ \-m
.IR monitor ]
.RB [ \-p
.IR prompt ]
.RB [ \-fn
.IR font ]
.RB [ \-nb
.IR color ]
.RB [ \-nf
.IR color ]
.RB [ \-sb
.IR color ]
.RB [ \-sf
.IR color ]
.RB [ \-w
.IR windowid ]
.P
.BR dmenu_run " ..."
.SH DESCRIPTION
.B dmenu
is a dynamic menu for X, which reads a list of newline\-separated items from
stdin. When the user selects an item and presses Return, their choice is printed
to stdout and dmenu terminates. Entering text will narrow the items to those
matching the tokens in the input.
.P
.B dmenu_run
is a script used by
.IR dwm (1)
which lists programs in the user's $PATH and runs the result in their $SHELL.
.SH OPTIONS
.TP
.B \-b
dmenu appears at the bottom of the screen.
.TP
.B \-f
dmenu grabs the keyboard before reading stdin if not reading from a tty. This
is faster, but will lock up X until stdin reaches end\-of\-file.
.TP
.B \-i
dmenu matches menu items case insensitively.
.TP
.BI \-l " lines"
dmenu lists items vertically, with the given number of lines.
.TP
.BI \-m " monitor"
dmenu is displayed on the monitor number supplied. Monitor numbers are starting
from 0.
.TP
.BI \-p " prompt"
defines the prompt to be displayed to the left of the input field.
.TP
.BI \-fn " font"
defines the font or font set used.
.TP
.BI \-nb " color"
defines the normal background color.
.IR #RGB ,
.IR #RRGGBB ,
and X color names are supported.
.TP
.BI \-nf " color"
defines the normal foreground color.
.TP
.BI \-sb " color"
defines the selected background color.
.TP
.BI \-sf " color"
defines the selected foreground color.
.TP
.B \-v
prints version information to stdout, then exits.
.TP
.BI \-w " windowid"
embed into windowid.
.SH USAGE
dmenu is completely controlled by the keyboard. Items are selected using the
arrow keys, page up, page down, home, and end.
.TP
.B Tab
Copy the selected item to the input field.
.TP
.B Return
Confirm selection. Prints the selected item to stdout and exits, returning
success.
.TP
.B Ctrl-Return
Confirm selection. Prints the selected item to stdout and continues.
.TP
.B Shift\-Return
Confirm input. Prints the input text to stdout and exits, returning success.
.TP
.B Escape
Exit without selecting an item, returning failure.
.TP
.B Ctrl-Left
Move cursor to the start of the current word
.TP
.B Ctrl-Right
Move cursor to the end of the current word
.TP
.B C\-a
Home
.TP
.B C\-b
Left
.TP
.B C\-c
Escape
.TP
.B C\-d
Delete
.TP
.B C\-e
End
.TP
.B C\-f
Right
.TP
.B C\-g
Escape
.TP
.B C\-h
Backspace
.TP
.B C\-i
Tab
.TP
.B C\-j
Return
.TP
.B C\-J
Shift-Return
.TP
.B C\-k
Delete line right
.TP
.B C\-m
Return
.TP
.B C\-M
Shift-Return
.TP
.B C\-n
Down
.TP
.B C\-p
Up
.TP
.B C\-u
Delete line left
.TP
.B C\-w
Delete word left
.TP
.B C\-y
Paste from primary X selection
.TP
.B C\-Y
Paste from X clipboard
.TP
.B M\-b
Move cursor to the start of the current word
.TP
.B M\-f
Move cursor to the end of the current word
.TP
.B M\-g
Home
.TP
.B M\-G
End
.TP
.B M\-h
Up
.TP
.B M\-j
Page down
.TP
.B M\-k
Page up
.TP
.B M\-l
Down
.SH SEE ALSO
.IR dwm (1),
.IR stest (1)

15
dmenu.c
View File

@ -60,8 +60,9 @@ static Clr *scheme[SchemeLast];
#include "config.h"
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
static char *(*fstrstr)(const char *, const char *) = strstr;
static char * cistrstr(const char *s, const char *sub);
static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
static char *(*fstrstr)(const char *, const char *) = cistrstr;
static unsigned int
textw_clamp(const char *str, unsigned int n)
@ -413,13 +414,13 @@ vi_keypress(KeySym ksym, const XKeyEvent *ev)
if (cursor)
cursor = nextrune(-1);
break;
case XK_t:
case XK_h:
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
calcoffsets();
}
break;
case XK_h:
case XK_t:
if (sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev;
calcoffsets();
@ -937,9 +938,9 @@ main(int argc, char *argv[])
topbar = 0;
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
fast = 1;
else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
fstrncmp = strncasecmp;
fstrstr = cistrstr;
else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
fstrncmp = strncmp;
fstrstr = strstr;
} else if (!strcmp(argv[i], "-vi")) {
vi_mode = 1;
using_vi_mode = start_mode;

View File

@ -25,7 +25,7 @@
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeCursor, SchemeLast }; /* color schemes */
struct item {
char *text;
@ -33,6 +33,11 @@ struct item {
int out;
};
typedef struct {
KeySym ksym;
unsigned int state;
} Key;
static char text[BUFSIZ] = "";
static char *embed;
static int bh, mw, mh;
@ -43,6 +48,7 @@ static struct item *items = NULL;
static struct item *matches, *matchend;
static struct item *prev, *curr, *next, *sel;
static int mon = -1, screen;
static unsigned int using_vi_mode = 0;
static Atom clip, utf8;
static Display *dpy;
@ -162,8 +168,16 @@ drawmenu(void)
drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
curpos = TEXTW(text) - TEXTW(&text[cursor]);
if ((curpos += lrpad / 2 - 1) < w) {
curpos += lrpad / 2 - 1;
if (using_vi_mode && text[0] != '\0') {
drw_setscheme(drw, scheme[SchemeCursor]);
char vi_char[] = {text[cursor], '\0'};
drw_text(drw, x + curpos, 0, TEXTW(vi_char) - lrpad, bh, 0, vi_char, 0);
} else if (using_vi_mode) {
drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, x + curpos, 2, lrpad / 2, bh - 4, 1, 0);
} else if (curpos < w) {
drw_setscheme(drw, scheme[SchemeSel]);
drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
}
@ -320,6 +334,181 @@ movewordedge(int dir)
}
}
static void
vi_keypress(KeySym ksym, const XKeyEvent *ev)
{
static const size_t quit_len = LENGTH(quit_keys);
if (ev->state & ControlMask) {
switch(ksym) {
/* movement */
case XK_d: /* fallthrough */
if (next) {
sel = curr = next;
calcoffsets();
goto draw;
} else
ksym = XK_G;
break;
case XK_u:
if (prev) {
sel = curr = prev;
calcoffsets();
goto draw;
} else
ksym = XK_g;
break;
case XK_p: /* fallthrough */
case XK_P: break;
case XK_c:
cleanup();
exit(1);
case XK_Return: /* fallthrough */
case XK_KP_Enter: break;
default: return;
}
}
switch(ksym) {
/* movement */
case XK_0:
cursor = 0;
break;
case XK_dollar:
if (text[cursor + 1] != '\0') {
cursor = strlen(text) - 1;
break;
}
break;
case XK_b:
movewordedge(-1);
break;
case XK_e:
cursor = nextrune(+1);
movewordedge(+1);
if (text[cursor] == '\0')
--cursor;
else
cursor = nextrune(-1);
break;
case XK_g:
if (sel == matches) {
break;
}
sel = curr = matches;
calcoffsets();
break;
case XK_G:
if (next) {
/* jump to end of list and position items in reverse */
curr = matchend;
calcoffsets();
curr = prev;
calcoffsets();
while (next && (curr = curr->right))
calcoffsets();
}
sel = matchend;
break;
case XK_d:
if (cursor)
cursor = nextrune(-1);
break;
case XK_t:
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
calcoffsets();
}
break;
case XK_h:
if (sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev;
calcoffsets();
}
break;
case XK_n:
if (text[cursor] != '\0' && text[cursor + 1] != '\0')
cursor = nextrune(+1);
else if (text[cursor] == '\0' && cursor)
--cursor;
break;
case XK_w:
movewordedge(+1);
if (text[cursor] != '\0' && text[cursor + 1] != '\0')
cursor = nextrune(+1);
else if (cursor)
--cursor;
break;
/* insertion */
case XK_a:
cursor = nextrune(+1);
/* fallthrough */
case XK_i:
using_vi_mode = 0;
break;
case XK_A:
if (text[cursor] != '\0')
cursor = strlen(text);
using_vi_mode = 0;
break;
case XK_I:
cursor = using_vi_mode = 0;
break;
case XK_p:
if (text[cursor] != '\0')
cursor = nextrune(+1);
XConvertSelection(dpy, (ev->state & ControlMask) ? clip : XA_PRIMARY,
utf8, utf8, win, CurrentTime);
return;
case XK_P:
XConvertSelection(dpy, (ev->state & ControlMask) ? clip : XA_PRIMARY,
utf8, utf8, win, CurrentTime);
return;
/* deletion */
case XK_K:
text[cursor] = '\0';
if (cursor)
cursor = nextrune(-1);
match();
break;
case XK_x:
cursor = nextrune(+1);
insert(NULL, nextrune(-1) - cursor);
if (text[cursor] == '\0' && text[0] != '\0')
--cursor;
match();
break;
/* misc. */
case XK_Return:
case XK_KP_Enter:
puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
if (!(ev->state & ControlMask)) {
cleanup();
exit(0);
}
if (sel)
sel->out = 1;
break;
case XK_Tab:
if (!sel)
return;
strncpy(text, sel->text, sizeof text - 1);
text[sizeof text - 1] = '\0';
cursor = strlen(text) - 1;
match();
break;
default:
for (size_t i = 0; i < quit_len; ++i)
if (quit_keys[i].ksym == ksym &&
(quit_keys[i].state & ev->state) == quit_keys[i].state) {
cleanup();
exit(1);
}
}
draw:
drawmenu();
}
static void
keypress(XKeyEvent *ev)
{
@ -339,6 +528,18 @@ keypress(XKeyEvent *ev)
break;
}
if (using_vi_mode) {
vi_keypress(ksym, ev);
return;
} else if (vi_mode &&
(ksym == global_esc.ksym &&
(ev->state & global_esc.state) == global_esc.state)) {
using_vi_mode = 1;
if (cursor)
cursor = nextrune(-1);
goto draw;
}
if (ev->state & ControlMask) {
switch(ksym) {
case XK_a: ksym = XK_Home; break;
@ -542,6 +743,8 @@ paste(void)
insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
XFree(p);
}
if (using_vi_mode && text[cursor] == '\0')
--cursor;
drawmenu();
}
@ -737,6 +940,11 @@ main(int argc, char *argv[])
else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
fstrncmp = strncasecmp;
fstrstr = cistrstr;
} else if (!strcmp(argv[i], "-vi")) {
vi_mode = 1;
using_vi_mode = start_mode;
global_esc.ksym = XK_Escape;
global_esc.state = 0;
} else if (i + 1 == argc)
usage();
/* these options take one argument */

13
dmenu.c.rej Normal file
View File

@ -0,0 +1,13 @@
@@ -714,9 +715,9 @@
topbar = 0;
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
fast = 1;
- else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
- fstrncmp = strncasecmp;
- fstrstr = cistrstr;
+ else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
+ fstrncmp = strncmp;
+ fstrstr = strstr;
} else if (i + 1 == argc)
usage();
/* these options take one argument */

BIN
dmenu.o

Binary file not shown.