commit - ddca21290abf5f18a0ece0af679320c330c70e74
commit + a022230da56d11e366e2474e157572d23f08283e
blob - edf0805ef8935bc3798b78485cf849196e8f7a62
blob + 7b170d7ebe56cfb069638fef287a9c2de0310d5e
--- rover.1
+++ rover.1
Open \fB$EDITOR\fR with the selected file.
.TP
.B /
-Start incremental search (\fB<RETURN>\fR to finish).
+Start incremental search.
.TP
.B f/d/s
Toggle file/directory/hidden listing.
.TP
.B 0-9
Change tab.
+.SH LINE EDITING
+.PP
+Some commands will prompt for an input string. For example, in order to rename a
+file, the user must supply the new name. This string will appear at the bottom
+of the screen and must be edited interactivelly using the keyboard. Printable
+keys will insert characters at the cursor position. The following shortcuts are
+available for line editing:
+.TP
+.B <TAB>
+Finish editing and \fBcancel\fR command.
+.TP
+.B <RETURN>
+Finish editing and \fBconfirm\fR command.
+.TP
+.B <LEFT>/<RIGHT>
+Move insertion cursor left/right.
+.TP
+.B <UP>/<DOWN>
+Move insertion cursor to beginning/end of string.
+.TP
+.B <BACKSPACE>
+Remove one character before cursor.
+.TP
+.B <DELETE>
+Remove one character after cursor.
+.TP
+.B <CONTROL>+u
+Clear line (remove all characters).
.SH ENVIRONMENT VARIABLES
.TP
.B HOME
blob - efacfe9d63b20d419939d7de80dc3c8e6760f29c
blob + 5ea6fb141975a53f97fc32fb7d362b31b0b68dc2
--- rover.c
+++ rover.c
static char STATUS[STATUSSZ];
#define INPUTSZ 256
static char INPUT[INPUTSZ];
+#define EDITSZ 256
/* Argument buffers for execvp(). */
#define MAXARGS 256
char **entries;
} Marks;
+/* Line editing state. */
+typedef struct Edit {
+ char buffer[EDITSZ];
+ int left, right;
+} Edit;
+
/* Global state. Some basic info is allocated for ten tabs. */
static struct Rover {
int tab;
WINDOW *window;
char cwd[10][PATH_MAX];
Marks marks;
+ Edit edit;
} rover;
/* Macros for accessing global state. */
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define ISDIR(E) (strchr((E), '/') != NULL)
+
+/* Line Editing Macros. */
+#define EDIT_FULL(E) ((E).left > (E).right)
+#define EDIT_CAN_LEFT(E) ((E).left)
+#define EDIT_CAN_RIGHT(E) ((E).right < EDITSZ-1)
+#define EDIT_LEFT(E) (E).buffer[(E).right--] = (E).buffer[--(E).left]
+#define EDIT_RIGHT(E) (E).buffer[(E).left++] = (E).buffer[++(E).right]
+#define EDIT_INSERT(E, C) (E).buffer[(E).left++] = (C)
+#define EDIT_BACKSPACE(E) (E).left--
+#define EDIT_DELETE(E) (E).right++
+#define EDIT_CLEAR(E) do { (E).left = 0; (E).right = EDITSZ-1; } while(0)
+typedef enum EditStat {CONTINUE, CONFIRM, CANCEL} EditStat;
typedef enum Color {DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, MAGENTA, WHITE} Color;
typedef int (*PROCESS)(const char *path);
return ret;
}
-/* Interactive getstr(). */
-static int
-igetstr(char *buffer, int maxlen)
+static void
+start_line_edit(const char *init_input)
{
- int ch, length;
-
- length = strlen(buffer);
curs_set(TRUE);
- ch = getch();
- if (ch == '\r' || ch == '\n' || ch == KEY_DOWN || ch == KEY_ENTER) {
+ strncpy(INPUT, init_input, INPUTSZ);
+ strncpy(rover.edit.buffer, init_input, EDITSZ);
+ rover.edit.left = strlen(init_input);
+ rover.edit.right = EDITSZ - 1;
+}
+
+/* Read input and change editing state accordingly. */
+static EditStat
+get_line_edit()
+{
+ int ch = getch();
+ if (ch == '\r' || ch == '\n' || ch == KEY_ENTER) {
curs_set(FALSE);
- return 0;
- } else if (ch == erasechar() || ch == KEY_LEFT || ch == KEY_BACKSPACE) {
- if (length)
- buffer[--length] = '\0';
- } else if (ch == killchar()) {
- length = 0;
- buffer[0] = '\0';
- } else if (length < maxlen - 1 && isprint(ch)) {
- buffer[length++] = ch;
- buffer[length] = '\0';
+ return CONFIRM;
+ } else if (ch == '\t') {
+ curs_set(FALSE);
+ return CANCEL;
+ } else if (EDIT_CAN_LEFT(rover.edit) && ch == KEY_LEFT) {
+ EDIT_LEFT(rover.edit);
+ } else if (EDIT_CAN_RIGHT(rover.edit) && ch == KEY_RIGHT) {
+ EDIT_RIGHT(rover.edit);
+ } else if (ch == KEY_UP) {
+ while (EDIT_CAN_LEFT(rover.edit)) EDIT_LEFT(rover.edit);
+ } else if (ch == KEY_DOWN) {
+ while (EDIT_CAN_RIGHT(rover.edit)) EDIT_RIGHT(rover.edit);
+ } else if (ch == erasechar() || ch == KEY_BACKSPACE) {
+ if (EDIT_CAN_LEFT(rover.edit)) EDIT_BACKSPACE(rover.edit);
+ } else if (ch == KEY_DC) {
+ if (EDIT_CAN_RIGHT(rover.edit)) EDIT_DELETE(rover.edit);
+ } else if (ch == killchar()) {
+ EDIT_CLEAR(rover.edit);
+ clear_message();
+ } else if (!EDIT_FULL(rover.edit) && isprint(ch)) {
+ EDIT_INSERT(rover.edit, ch);
}
- return 1;
+ /* Copy edit contents to INPUT and append null character. */
+ strncpy(INPUT, rover.edit.buffer, MIN(rover.edit.left, INPUTSZ-1));
+ strncpy(&INPUT[rover.edit.left], &rover.edit.buffer[rover.edit.right+1],
+ MIN(EDITSZ-rover.edit.right-1, INPUTSZ-rover.edit.left-1));
+ INPUT[MIN(rover.edit.left+EDITSZ-rover.edit.right-1, INPUTSZ-1)] = '\0';
+ return CONTINUE;
}
/* Update line input on the screen. */
mvaddstr(LINES - 1, 0, prompt);
color_set(color, NULL);
mvaddstr(LINES - 1, plen, INPUT);
- mvaddch(LINES - 1, ilen + plen, ' ');
- move(LINES - 1, ilen + plen);
+ mvaddch(LINES - 1, plen + ilen, ' ');
+ move(LINES - 1, plen + rover.edit.left);
color_set(DEFAULT, NULL);
}
char *program;
const char *key;
DIR *d;
+ EditStat edit_stat;
if (argc == 2) {
if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) {
if (!rover.nfiles) continue;
oldsel = ESEL;
oldscroll = SCROLL;
- strcpy(INPUT, "");
+ start_line_edit("");
update_input(prompt, DEFAULT);
- while (igetstr(INPUT, INPUTSZ)) {
+ while ((edit_stat = get_line_edit()) == CONTINUE) {
int sel;
Color color = RED;
length = strlen(INPUT);
update_view();
update_input(prompt, color);
}
+ if (edit_stat == CANCEL) {
+ ESEL = oldsel;
+ SCROLL = oldscroll;
+ }
clear_message();
update_view();
} else if (!strcmp(key, RVK_TG_FILES)) {
} else if (!strcmp(key, RVK_NEW_FILE)) {
int ok = 0;
char *prompt = "new file: ";
- strcpy(INPUT, "");
+ start_line_edit("");
update_input(prompt, DEFAULT);
- while (igetstr(INPUT, INPUTSZ)) {
+ while ((edit_stat = get_line_edit()) == CONTINUE) {
int length = strlen(INPUT);
ok = 1;
for (i = 0; i < rover.nfiles; i++) {
update_input(prompt, ok ? GREEN : RED);
}
clear_message();
- if (strlen(INPUT)) {
+ if (edit_stat == CONFIRM && strlen(INPUT)) {
if (ok) {
addfile(INPUT);
cd(1);
} else if (!strcmp(key, RVK_NEW_DIR)) {
int ok = 0;
char *prompt = "new directory: ";
- strcpy(INPUT, "");
+ start_line_edit("");
update_input(prompt, DEFAULT);
- while (igetstr(INPUT, INPUTSZ)) {
+ while ((edit_stat = get_line_edit()) == CONTINUE) {
int length = strlen(INPUT);
ok = 1;
for (i = 0; i < rover.nfiles; i++) {
update_input(prompt, ok ? GREEN : RED);
}
clear_message();
- if (strlen(INPUT)) {
+ if (edit_stat == CONFIRM && strlen(INPUT)) {
if (ok) {
adddir(INPUT);
cd(1);
last = INPUT + strlen(INPUT) - 1;
if ((isdir = *last == '/'))
*last = '\0';
+ start_line_edit(INPUT);
update_input(prompt, RED);
- while (igetstr(INPUT, INPUTSZ)) {
+ while ((edit_stat = get_line_edit()) == CONTINUE) {
int length = strlen(INPUT);
ok = 1;
for (i = 0; i < rover.nfiles; i++)
update_input(prompt, ok ? GREEN : RED);
}
clear_message();
- if (strlen(INPUT)) {
+ if (edit_stat == CONFIRM && strlen(INPUT)) {
if (isdir)
strcat(INPUT, "/");
if (ok) {