summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 059f7d6)
raw | patch | inline | side by side (parent: 059f7d6)
author | Jonas Fonseca <fonseca@diku.dk> | |
Thu, 27 Nov 2008 16:29:03 +0000 (17:29 +0100) | ||
committer | Jonas Fonseca <fonseca@diku.dk> | |
Tue, 13 Jan 2009 21:55:17 +0000 (22:55 +0100) |
Adds support for new %(file), %(directory) and %(ref) identifiers.
Also adds infrastructure for working with argument arrays
Also adds infrastructure for working with argument arrays
NEWS | patch | blob | history | |
manual.txt | patch | blob | history | |
tig.c | patch | blob | history | |
tigrc.5.txt | patch | blob | history |
index ece7619a2bbfe3fdfc1d97d7676d1880b4d1770e..3532b5dbe7b3faabebe4d3940eb4293bc8c3790c 100644 (file)
--- a/NEWS
+++ b/NEWS
tracked remotes, remotes.
- Add bash completion for blame.
- Tree view: edit files of the current branch.
+ - Run requests: new identifiers %(directory), %(file), and %(ref)
Bug fixes:
diff --git a/manual.txt b/manual.txt
index 447dec07020fe8f6f8906a51edb0e6f53de7f97a..8ac7cd28c90e723b5d7bd5c1ae834d407e4721ca 100644 (file)
--- a/manual.txt
+++ b/manual.txt
%(head) The currently viewed 'head' ID. Defaults to HEAD
%(commit) The currently selected commit ID.
%(blob) The currently selected blob ID.
+%(directory) The current directory path in the tree view; \
+ empty for the root directory.
+%(file) The currently selected file.
+%(ref) The reference given to blame or HEAD if undefined.
------------------------------------------------------------------------------
[[title-window]]
index 6486530dc7373b1681fde26b4c5156319dbe5714..3fce6f086685395490beacd03f7afb968c5c7670 100644 (file)
--- a/tig.c
+++ b/tig.c
static struct ref **get_refs(const char *id);
+enum format_flags {
+ FORMAT_ALL, /* Perform replacement in all arguments. */
+ FORMAT_DASH, /* Perform replacement up until "--". */
+ FORMAT_NONE /* No replacement should be performed. */
+};
+
+static bool format_command(char dst[], const char *src[], enum format_flags flags);
+static bool format_argv(const char *dst[], const char *src[], enum format_flags flags);
+
struct int_map {
const char *name;
int namelen;
return bufsize;
}
+static bool
+argv_from_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
+{
+ int valuelen;
+
+ while (*cmd && *argc < SIZEOF_ARG && (valuelen = strcspn(cmd, " \t"))) {
+ bool advance = cmd[valuelen] != 0;
+
+ cmd[valuelen] = 0;
+ argv[(*argc)++] = chomp_string(cmd);
+ cmd += valuelen + advance;
+ }
+
+ if (*argc < SIZEOF_ARG)
+ argv[*argc] = NULL;
+ return *argc < SIZEOF_ARG;
+}
+
/*
* Executing external commands.
struct run_request {
enum keymap keymap;
int key;
- char cmd[SIZEOF_STR];
+ const char *argv[SIZEOF_ARG];
};
static struct run_request *run_request;
add_run_request(enum keymap keymap, int key, int argc, const char **argv)
{
struct run_request *req;
- char cmd[SIZEOF_STR];
- size_t bufpos;
- for (bufpos = 0; argc > 0; argc--, argv++)
- if (!string_format_from(cmd, &bufpos, "%s ", *argv))
- return REQ_NONE;
+ if (argc >= ARRAY_SIZE(req->argv) - 1)
+ return REQ_NONE;
req = realloc(run_request, (run_requests + 1) * sizeof(*run_request));
if (!req)
return REQ_NONE;
run_request = req;
- req = &run_request[run_requests++];
- string_copy(req->cmd, cmd);
+ req = &run_request[run_requests];
req->keymap = keymap;
req->key = key;
+ req->argv[0] = NULL;
- return REQ_NONE + run_requests;
+ if (!format_argv(req->argv, argv, FORMAT_NONE))
+ return REQ_NONE;
+
+ return REQ_NONE + ++run_requests;
}
static struct run_request *
static void
add_builtin_run_requests(void)
{
+ const char *cherry_pick[] = { "git", "cherry-pick", "%(commit)", NULL };
+ const char *gc[] = { "git", "gc", NULL };
struct {
enum keymap keymap;
int key;
- const char *argv[1];
+ int argc;
+ const char **argv;
} reqs[] = {
- { KEYMAP_MAIN, 'C', { "git cherry-pick %(commit)" } },
- { KEYMAP_GENERIC, 'G', { "git gc" } },
+ { KEYMAP_MAIN, 'C', ARRAY_SIZE(cherry_pick) - 1, cherry_pick },
+ { KEYMAP_GENERIC, 'G', ARRAY_SIZE(gc) - 1, gc },
};
int i;
for (i = 0; i < ARRAY_SIZE(reqs); i++) {
enum request req;
- req = add_run_request(reqs[i].keymap, reqs[i].key, 1, reqs[i].argv);
+ req = add_run_request(reqs[i].keymap, reqs[i].key, reqs[i].argc, reqs[i].argv);
if (req != REQ_NONE)
add_keybinding(reqs[i].keymap, req, reqs[i].key);
}
set_option(const char *opt, char *value)
{
const char *argv[SIZEOF_ARG];
- int valuelen;
int argc = 0;
- /* Tokenize */
- while (argc < ARRAY_SIZE(argv) && (valuelen = strcspn(value, " \t"))) {
- argv[argc++] = value;
- value += valuelen;
-
- /* Nothing more to tokenize or last available token. */
- if (!*value || argc >= ARRAY_SIZE(argv))
- break;
-
- *value++ = 0;
- while (isspace(*value))
- value++;
+ if (!argv_from_string(argv, &argc, value)) {
+ config_msg = "Too many option arguments";
+ return ERR;
}
if (!strcmp(opt, "color"))
view->vid[0] = 0;
}
+static void
+free_argv(const char *argv[])
+{
+ int argc;
+
+ for (argc = 0; argv[argc]; argc++)
+ free((void *) argv[argc]);
+}
+
+static bool
+format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags flags)
+{
+ char buf[SIZEOF_STR];
+ int argc;
+ bool noreplace = flags == FORMAT_NONE;
+
+ free_argv(dst_argv);
+
+ for (argc = 0; src_argv[argc]; argc++) {
+ const char *arg = src_argv[argc];
+ size_t bufpos = 0;
+
+ while (arg) {
+ char *next = strstr(arg, "%(");
+ int len = next - arg;
+ const char *value;
+
+ if (!next || noreplace) {
+ if (flags == FORMAT_DASH && !strcmp(arg, "--"))
+ noreplace = TRUE;
+ len = strlen(arg);
+ value = "";
+
+ } else if (!prefixcmp(next, "%(directory)")) {
+ value = opt_path;
+
+ } else if (!prefixcmp(next, "%(file)")) {
+ value = opt_file;
+
+ } else if (!prefixcmp(next, "%(ref)")) {
+ value = *opt_ref ? opt_ref : "HEAD";
+
+ } else if (!prefixcmp(next, "%(head)")) {
+ value = ref_head;
+
+ } else if (!prefixcmp(next, "%(commit)")) {
+ value = ref_commit;
+
+ } else if (!prefixcmp(next, "%(blob)")) {
+ value = ref_blob;
+
+ } else {
+ report("Unknown replacement: `%s`", next);
+ return FALSE;
+ }
+
+ if (!string_format_from(buf, &bufpos, "%.*s%s", len, arg, value))
+ return FALSE;
+
+ arg = next && !noreplace ? strchr(next, ')') + 1 : NULL;
+ }
+
+ dst_argv[argc] = strdup(buf);
+ if (!dst_argv[argc])
+ break;
+ }
+
+ dst_argv[argc] = NULL;
+
+ return src_argv[argc] == NULL;
+}
+
+static bool
+format_command(char dst[], const char *src_argv[], enum format_flags flags)
+{
+ const char *dst_argv[SIZEOF_ARG * 2] = { NULL };
+ int bufsize = 0;
+ int argc;
+
+ if (!format_argv(dst_argv, src_argv, flags)) {
+ free_argv(dst_argv);
+ return FALSE;
+ }
+
+ for (argc = 0; dst_argv[argc] && bufsize < SIZEOF_STR; argc++) {
+ if (bufsize > 0)
+ dst[bufsize++] = ' ';
+ bufsize = sq_quote(dst, bufsize, dst_argv[argc]);
+ }
+
+ if (bufsize < SIZEOF_STR)
+ dst[bufsize] = 0;
+ free_argv(dst_argv);
+
+ return src_argv[argc] == NULL && bufsize < SIZEOF_STR;
+}
+
static void
end_update(struct view *view, bool force)
{
{
struct run_request *req = get_run_request(request);
char buf[SIZEOF_STR * 2];
- size_t bufpos;
- char *cmd;
if (!req) {
report("Unknown run request");
return;
}
- bufpos = 0;
- cmd = req->cmd;
-
- while (cmd) {
- char *next = strstr(cmd, "%(");
- int len = next - cmd;
- char *value;
-
- if (!next) {
- len = strlen(cmd);
- value = "";
-
- } else if (!strncmp(next, "%(head)", 7)) {
- value = ref_head;
-
- } else if (!strncmp(next, "%(commit)", 9)) {
- value = ref_commit;
-
- } else if (!strncmp(next, "%(blob)", 7)) {
- value = ref_blob;
-
- } else {
- report("Unknown replacement in run request: `%s`", req->cmd);
- return;
- }
-
- if (!string_format_from(buf, &bufpos, "%.*s%s", len, cmd, value))
- return;
-
- if (next)
- next = strchr(next, ')') + 1;
- cmd = next;
- }
-
- open_external_viewer(buf);
+ if (format_command(buf, req->argv, FORMAT_ALL))
+ open_external_viewer(buf);
}
/*
for (i = 0; i < run_requests; i++) {
struct run_request *req = get_run_request(REQ_NONE + i + 1);
const char *key;
+ char cmd[SIZEOF_STR];
+ size_t bufpos;
+ int argc;
if (!req)
continue;
if (!*key)
key = "(no key defined)";
+ for (bufpos = 0, argc = 0; req->argv[argc]; argc++)
+ if (!string_format_from(cmd, &bufpos, "%s%s",
+ argc ? " " : "", req->argv[argc]))
+ return REQ_NONE;
+
if (!string_format(buf, " %-10s %-14s `%s`",
- keymap_table[req->keymap].name,
- key, req->cmd))
+ keymap_table[req->keymap].name, key, cmd))
continue;
add_line_text(view, buf, LINE_DEFAULT);
diff --git a/tigrc.5.txt b/tigrc.5.txt
index 46656499b89c64788f072ac422eaec77d1254519..7c1daece455d3bae71bbd034d17d7e87c6c4d31a 100644 (file)
--- a/tigrc.5.txt
+++ b/tigrc.5.txt
%(head) The currently viewed 'head' ID. Defaults to HEAD
%(commit) The currently selected commit ID.
%(blob) The currently selected blob ID.
+%(directory) The current directory path in the tree view; \
+ empty for the root directory.
+%(file) The currently selected file.
+%(ref) The reference given to blame or HEAD if undefined.
------------------------------------------------------------------------------
As an example, the following external command will save the current commit as