summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonas Fonseca <jonas.fonseca@gmail.com>2014-03-02 22:36:29 -0500
committerJonas Fonseca <jonas.fonseca@gmail.com>2014-03-06 19:49:58 -0500
commit237a269c8e14248ff903b50f7c6624f247626ea5 (patch)
tree1e0c0b6e9720ee052ffa08847b3c795e1c0afb3e
parentb17610f27b56d7aa4c7af27e172b76e1ee1e99db (diff)
Add multibyte support to struct key_input
Fixes #99 Closes #105
-rw-r--r--doc/tigrc.5.adoc11
-rw-r--r--include/tig/keys.h14
-rw-r--r--src/display.c74
-rw-r--r--src/keys.c46
-rw-r--r--src/tig.c2
5 files changed, 108 insertions, 39 deletions
diff --git a/doc/tigrc.5.adoc b/doc/tigrc.5.adoc
index 39f85525..d05c10d3 100644
--- a/doc/tigrc.5.adoc
+++ b/doc/tigrc.5.adoc
@@ -335,6 +335,9 @@ bind status + !git commit --amend
# User-defined internal command that reloads ~/.tigrc
bind generic S :source ~/.tigrc
+
+# UTF8-encoded characters can be used as key values.
+bind generic ΓΈ @sh -c "printf '%s' %(commit) | pbcopy"
--------------------------------------------------------------------------
Or in the Git configuration files:
@@ -362,10 +365,10 @@ to set key mapping in all keymaps.
Key values::
-Key values should never be quoted. Use either the ASCII value or one of the
-following symbolic key names. Symbolic key names are case insensitive, Use
-*Hash* to bind to the `#` key, since the hash mark is used as a comment
-character.
+Key values should never be quoted. Use either an ASCII or UTF8-encoded character
+or one of the following symbolic key names. Symbolic key names are case
+insensitive. Use *Hash* to bind to the `#` key, since the hash mark is used as a
+comment character.
*Enter*, *Space*, *Backspace*, *Tab*, *Escape*, *Left*, *Right*, *Up*, *Down*,
*Insert*, *Delete*, *Hash*, *Home*, *End*, *PageUp*, *PageDown*, *F1*, *F2*, *F3*,
diff --git a/include/tig/keys.h b/include/tig/keys.h
index 49ca2c65..77dcf50b 100644
--- a/include/tig/keys.h
+++ b/include/tig/keys.h
@@ -32,13 +32,25 @@ struct keymap {
};
struct key_input {
- int key;
+ union {
+ int key;
+ char bytes[7];
+ } data;
struct {
bool escape:1;
bool control:1;
+ bool multibytes:1;
} modifiers;
};
+static inline unsigned long
+key_input_to_unicode(struct key_input *input)
+{
+ return input->modifiers.multibytes
+ ? utf8_to_unicode(input->data.bytes, strlen(input->data.bytes))
+ : 0;
+}
+
void add_keymap(struct keymap *keymap);
struct keymap *get_keymap(const char *name, size_t namelen);
struct keymap *get_keymaps(void);
diff --git a/src/display.c b/src/display.c
index 2464489b..f9265abd 100644
--- a/src/display.c
+++ b/src/display.c
@@ -435,24 +435,57 @@ get_input(int prompt_position, struct key_input *input, bool modifiers)
redraw_display(TRUE);
} else {
+ int pos, key_length;
+
input_mode = FALSE;
if (key == erasechar())
key = KEY_BACKSPACE;
- input->key = key;
- return input->key;
+
+ /*
+ * Ctrl-<key> values are represented using a 0x1F
+ * bitmask on the key value. To 'unmap' we assume that:
+ *
+ * - Ctrl-Z is handled by Ncurses.
+ * - Ctrl-m is the same as Return/Enter.
+ * - Ctrl-i is the same as Tab.
+ *
+ * For all other key values in the range the Ctrl flag
+ * is set and the key value is updated to the proper
+ * ASCII value.
+ */
+ if (KEY_CTL('a') <= key && key <= KEY_CTL('x') && key != KEY_RETURN && key != KEY_TAB) {
+ input->modifiers.control = 1;
+ key = key | 0x40;
+ }
+
+ if ((key >= KEY_MIN && key < KEY_MAX) || key < 0x1F) { // || key == ' ') {
+ input->data.key = key;
+ return input->data.key;
+ }
+
+ input->modifiers.multibytes = 1;
+ input->data.bytes[0] = key;
+
+ key_length = utf8_char_length(input->data.bytes);
+ for (pos = 1; pos < key_length && pos < sizeof(input->data.bytes) - 1; pos++) {
+ input->data.bytes[pos] = wgetch(status_win);
+ }
+
+ return OK;
}
}
}
-typedef enum input_status (*input_handler)(void *data, char *buf, int c);
+typedef enum input_status (*input_handler)(void *data, char *buf, struct key_input *input);
static char *
prompt_input(const char *prompt, input_handler handler, void *data)
{
enum input_status status = INPUT_OK;
static char buf[SIZEOF_STR];
+ unsigned char chars_length[SIZEOF_STR];
struct key_input input;
- size_t pos = 0;
+ size_t pos = 0, chars = 0;
buf[pos] = 0;
@@ -468,10 +501,14 @@ prompt_input(const char *prompt, input_handler handler, void *data)
break;
case KEY_BACKSPACE:
- if (pos > 0)
- buf[--pos] = 0;
- else
+ if (pos > 0) {
+ int len = chars_length[--chars];
+
+ pos -= len;
+ buf[pos] = 0;
+ } else {
status = INPUT_CANCEL;
+ }
break;
case KEY_ESC:
@@ -484,9 +521,14 @@ prompt_input(const char *prompt, input_handler handler, void *data)
return NULL;
}
- status = handler(data, buf, input.key);
- if (status == INPUT_OK)
- buf[pos++] = (char) input.key;
+ status = handler(data, buf, &input);
+ if (status == INPUT_OK) {
+ int len = strlen(input.data.bytes);
+
+ string_ncopy_do(buf + pos, sizeof(buf) - pos, input.data.bytes, len);
+ pos += len;
+ chars_length[chars++] = len;
+ }
}
}
@@ -503,8 +545,10 @@ prompt_input(const char *prompt, input_handler handler, void *data)
}
static enum input_status
-prompt_yesno_handler(void *data, char *buf, int c)
+prompt_yesno_handler(void *data, char *buf, struct key_input *input)
{
+ unsigned long c = key_input_to_unicode(input);
+
if (c == 'y' || c == 'Y')
return INPUT_STOP;
if (c == 'n' || c == 'N')
@@ -524,9 +568,11 @@ prompt_yesno(const char *prompt)
}
static enum input_status
-read_prompt_handler(void *data, char *buf, int c)
+read_prompt_handler(void *data, char *buf, struct key_input *input)
{
- return isprint(c) ? INPUT_OK : INPUT_SKIP;
+ unsigned long c = key_input_to_unicode(input);
+
+ return unicode_width(c, 8) ? INPUT_OK : INPUT_SKIP;
}
char *
@@ -583,7 +629,7 @@ prompt_menu(const char *prompt, const struct menu_item *items, int *selected)
default:
for (i = 0; items[i].text; i++)
- if (items[i].hotkey == input.key) {
+ if (items[i].hotkey == input.data.bytes[0]) {
*selected = i;
status = INPUT_STOP;
break;
diff --git a/src/keys.c b/src/keys.c
index a792d4d6..28cf8374 100644
--- a/src/keys.c
+++ b/src/keys.c
@@ -90,7 +90,7 @@ get_keybinding(struct keymap *keymap, struct key_input *input)
if (!memcmp(&generic_keymap.data[i].input, input, sizeof(*input)))
return generic_keymap.data[i].request;
- return (enum request) input->key;
+ return REQ_NONE;
}
@@ -141,7 +141,15 @@ get_key_value(const char *name, struct key_input *input)
for (i = 0; i < ARRAY_SIZE(key_table); i++)
if (!strcasecmp(key_table[i].name, name)) {
- input->key = key_table[i].value;
+ if (key_table[i].value == ' ') {
+ name = " ";
+ break;
+ }
+ if (key_table[i].value == '#') {
+ name = "#";
+ break;
+ }
+ input->data.key = key_table[i].value;
return OK;
}
@@ -153,8 +161,10 @@ get_key_value(const char *name, struct key_input *input)
name += 1;
}
- if (strlen(name) == 1 && isprint(*name)) {
- input->key = *name;
+ i = utf8_char_length(name);
+ if (strlen(name) == i && utf8_to_unicode(name, i) != 0) {
+ strncpy(input->data.bytes, name, i);
+ input->modifiers.multibytes = 1;
return OK;
}
@@ -164,25 +174,23 @@ get_key_value(const char *name, struct key_input *input)
const char *
get_key_name(const struct key_input *input)
{
+ static char buf[SIZEOF_STR];
+ const char *modifier = "";
int key;
- for (key = 0; key < ARRAY_SIZE(key_table); key++)
- if (key_table[key].value == input->key)
- return key_table[key].name;
-
- if (input->key >= 0x20 && input->key < 0x7f) {
- static char buf[SIZEOF_STR];
- const char *modifier = "";
- char key_char = input->key;
+ if (!input->modifiers.multibytes) {
+ for (key = 0; key < ARRAY_SIZE(key_table); key++)
+ if (key_table[key].value == input->data.key)
+ return key_table[key].name;
+ }
- if (input->modifiers.escape)
- modifier = "^[";
- else if (input->modifiers.control)
- modifier = "^";
+ if (input->modifiers.escape)
+ modifier = "^[";
+ else if (input->modifiers.control)
+ modifier = "^";
- if (string_format(buf, "'%s%c'", modifier, key_char))
- return buf;
- }
+ if (string_format(buf, "'%s%s'", modifier, input->data.bytes))
+ return buf;
return "(no key)";
}
diff --git a/src/tig.c b/src/tig.c
index 037dde80..518ad7b8 100644
--- a/src/tig.c
+++ b/src/tig.c
@@ -666,7 +666,7 @@ run_prompt_command(struct view *view, char *cmd)
}
} else if (cmd && strlen(cmd) == 1) {
- struct key_input input = { cmd[0] };
+ struct key_input input = { { cmd[0] } };
return get_keybinding(&view->ops->keymap, &input);