Skip to content

Commit

Permalink
implement user-programmable registers
Browse files Browse the repository at this point in the history
 * %(register:r) expansion variable
 * set-register r value
 * !>(r)echo arbitrary shell command
  • Loading branch information
rolandwalker committed Aug 15, 2017
1 parent d743cfd commit a07220c
Show file tree
Hide file tree
Showing 15 changed files with 482 additions and 20 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ TIG_OBJS = \
src/stash.o \
src/grep.o \
src/ui.o \
src/registers.o \
$(GRAPH_OBJS) \
$(COMPAT_OBJS)

Expand Down
5 changes: 4 additions & 1 deletion doc/manual.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ following variables.
|%(repo:is-inside-work-tree)
|Whether Tig is running inside a work tree,
either `true` or `false`.
|%(register:x) |A user-defined register value, where `x` is an ASCII
character.
|=============================================================================

Example user-defined commands:
Expand Down Expand Up @@ -488,7 +490,8 @@ Prompt
|:script <file> |Execute commands from `<file>`.
|:exec <flags><args...> |Execute command using `<args>` with external
user-defined command option flags defined in `<flags>`.
|:echo <args...> |Display text in the status bar.
|:echo <args...> |Display text in the status bar.
|:set-register <char> <args...> |Load a value into the register named `<char>`.
|=============================================================================

[[external-commands]]
Expand Down
4 changes: 4 additions & 0 deletions doc/tigrc.5.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,8 @@ the command that should be executed.
of output to the status bar.
|? |Prompt the user before executing the command.
|< |Exit Tig after executing the command.
|>(x) |Run the command synchronously, and store the first
line of output in the register named `x`.
|=============================================================================

Unless otherwise specified, commands are run in the foreground with their
Expand Down Expand Up @@ -700,6 +702,8 @@ following variable names, which are substituted before commands are run:
|%(repo:is-inside-work-tree)
|Whether Tig is running inside a work tree,
either `true` or `false`.
|%(register:x) |A user-defined register value, where `x` is an ASCII
character.
|=============================================================================

Examples:
Expand Down
2 changes: 2 additions & 0 deletions include/tig/argv.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define TIG_ARGV_H

#include "tig/tig.h"
#include "tig/registers.h"

/*
* Argument array helpers.
Expand Down Expand Up @@ -62,6 +63,7 @@ typedef unsigned long argv_number;
struct argv_env {
ARGV_ENV_INFO(ARGV_ENV_FIELDS)
unsigned long goto_lineno;
char *registers[SIZEOF_REGISTERS];
char search[SIZEOF_STR];
char none[1];
};
Expand Down
2 changes: 1 addition & 1 deletion include/tig/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ bool save_view(struct view *view, const char *path);
bool vertical_split_is_enabled(enum vertical_split vsplit, int height, int width);
int apply_vertical_split(int base_width);

bool open_external_viewer(const char *argv[], const char *dir, bool silent, bool confirm, bool echo, bool refresh, const char *notice);
bool open_external_viewer(const char *argv[], const char *dir, bool silent, bool confirm, bool echo, char register_key, bool refresh, const char *notice);
void open_editor(const char *file, unsigned int lineno);
void enable_mouse(bool enable);

Expand Down
1 change: 1 addition & 0 deletions include/tig/keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ struct run_request_flags {
bool exit;
bool internal;
bool echo;
char register_key;
};

struct run_request {
Expand Down
159 changes: 159 additions & 0 deletions include/tig/registers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/* Copyright (c) 2006-2017 Jonas Fonseca <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#ifndef TIG_REGISTERS_H
#define TIG_REGISTERS_H

#include "tig/types.h"

/* Index 0 of the register array, corresponding to character key ASCII
* space, is kept empty and reserved for internal use as "no register".
*/
#define REGISTER_KEY_MIN '!' /* corresponding to index 1 */
#define REGISTER_KEY_MAX '~' /* corresponding to index 94 */
#define REGISTER_KEY_OFFSET 0x20
#define SIZEOF_REGISTERS 1 + REGISTER_KEY_MAX - REGISTER_KEY_OFFSET

#define REGISTER_FLAG_OPEN_STR ">("
#define REGISTER_FLAG_CLOSE_STR ")"
#define REGISTER_ESC_CHAR '\\'

#define is_register_esc_char(ch) \
((ch) == REGISTER_ESC_CHAR)

#define is_register_meta_char(ch) \
(is_register_esc_char(ch) \
|| ((ch) == REGISTER_FLAG_OPEN_STR[1]) \
|| ((ch) == REGISTER_FLAG_CLOSE_STR[0]) \
|| ((ch) == '"') \
|| ((ch) == '\''))

#define at_register_flag_open(p) \
(((p)[0] == REGISTER_FLAG_OPEN_STR[0]) && ((p)[1] == REGISTER_FLAG_OPEN_STR[1]))

#define at_register_flag_close(p) \
((p)[0] == REGISTER_FLAG_CLOSE_STR[0])

#define at_register_escd_pair(p) \
(is_register_esc_char((p)[0]) && is_register_meta_char((p)[1]))

#define register_key_to_index(key) \
((((key) >= REGISTER_KEY_MIN) && ((key) <= REGISTER_KEY_MAX)) ? (unsigned int) (key) - REGISTER_KEY_OFFSET : 0)

bool register_set(const char key, const char *value);
const char *register_get(const char key);

/* metacharacters occur twice, once as an escaped sequence */
#define REGISTER_INFO(_) \
_("\\\\", '\\') \
_("\\(", '(') \
_("\\)", ')') \
_("\\\"", '"') \
_("\\'", '\'') \
_("!", '!') \
_("\"", '"') \
_("#", '#') \
_("$", '$') \
_("%", '%') \
_("&", '&') \
_("'", '\'') \
_("(", '(') \
_(")", ')') \
_("*", '*') \
_("+", '+') \
_(",", ',') \
_("-", '-') \
_(".", '.') \
_("/", '/') \
_("0", '0') \
_("1", '1') \
_("2", '2') \
_("3", '3') \
_("4", '4') \
_("5", '5') \
_("6", '6') \
_("7", '7') \
_("8", '8') \
_("9", '9') \
_(":", ':') \
_(";", ';') \
_("<", '<') \
_("=", '=') \
_(">", '>') \
_("?", '?') \
_("@", '@') \
_("A", 'A') \
_("B", 'B') \
_("C", 'C') \
_("D", 'D') \
_("E", 'E') \
_("F", 'F') \
_("G", 'G') \
_("H", 'H') \
_("I", 'I') \
_("J", 'J') \
_("K", 'K') \
_("L", 'L') \
_("M", 'M') \
_("N", 'N') \
_("O", 'O') \
_("P", 'P') \
_("Q", 'Q') \
_("R", 'R') \
_("S", 'S') \
_("T", 'T') \
_("U", 'U') \
_("V", 'V') \
_("W", 'W') \
_("X", 'X') \
_("Y", 'Y') \
_("Z", 'Z') \
_("[", '[') \
_("\\", '\\') \
_("]", ']') \
_("^", '^') \
_("_", '_') \
_("`", '`') \
_("a", 'a') \
_("b", 'b') \
_("c", 'c') \
_("d", 'd') \
_("e", 'e') \
_("f", 'f') \
_("g", 'g') \
_("h", 'h') \
_("i", 'i') \
_("j", 'j') \
_("k", 'k') \
_("l", 'l') \
_("m", 'm') \
_("n", 'n') \
_("o", 'o') \
_("p", 'p') \
_("q", 'q') \
_("r", 'r') \
_("s", 's') \
_("t", 't') \
_("u", 'u') \
_("v", 'v') \
_("w", 'w') \
_("x", 'x') \
_("y", 'y') \
_("z", 'z') \
_("{", '{') \
_("|", '|') \
_("}", '}') \
_("~", '~')

#endif
/* vim: set ts=8 sw=8 noexpandtab: */
11 changes: 11 additions & 0 deletions src/argv.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "tig/repo.h"
#include "tig/options.h"
#include "tig/prompt.h"
#include "tig/registers.h"

static bool
concat_argv(const char *argv[], char *buf, size_t buflen, const char *sep, bool quoted)
Expand Down Expand Up @@ -376,6 +377,12 @@ format_append_arg(struct format_context *format, const char ***dst_argv, const c
while (arg) {
const char *var = strstr(arg, "%(");
const char *closing = var ? strchr(var, ')') : NULL;

/* todo hardcoding is robust and compact but ugly */
if (var &&
(!strcmp(var, "%(register:\\))") || !strcmp(var, "%(register:))")))
closing++;

const char *next = closing ? closing + 1 : NULL;
const int len = var ? var - arg : strlen(arg);

Expand Down Expand Up @@ -468,6 +475,10 @@ argv_format(struct argv_env *argv_env, const char ***dst_argv, const char *src_a
#define FORMAT_REPO_VAR(type, name) \
{ "%(repo:" #name ")", STRING_SIZE("%(repo:" #name ")"), type ## _formatter, &repo.name, "" },
REPO_INFO(FORMAT_REPO_VAR)
#define FORMAT_REGISTER_VAR(namestr, keychar) \
{ "%(register:" namestr ")", STRING_SIZE("%(register:" namestr ")"), argv_string_formatter, \
argv_env->registers[ MIN(keychar,REGISTER_KEY_MAX) - REGISTER_KEY_OFFSET ], "" },
REGISTER_INFO(FORMAT_REGISTER_VAR)
};
struct format_context format = { vars, ARRAY_SIZE(vars), "", 0, file_filter };
int argc;
Expand Down
21 changes: 16 additions & 5 deletions src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "tig/draw.h"
#include "tig/display.h"
#include "tig/watch.h"
#include "tig/registers.h"

static void set_terminal_modes(void);

Expand Down Expand Up @@ -56,19 +57,29 @@ open_script(const char *path)
}

bool
open_external_viewer(const char *argv[], const char *dir, bool silent, bool confirm, bool echo, bool refresh, const char *notice)
open_external_viewer(const char *argv[], const char *dir, bool silent, bool confirm, bool echo, char register_key, bool refresh, const char *notice)
{
bool ok;

if (echo) {
if (echo || register_key) {
char buf[SIZEOF_STR] = "";

io_run_buf(argv, buf, sizeof(buf), dir, false);
if (*buf) {
report("%s", buf);
if (register_key)
register_set(register_key, buf);
if (echo)
report("%s", buf);
else
report_clear();
return true;
} else {
report("No output");
if (register_key)
register_set(register_key, "");
if (echo)
report("No output");
else
report_clear();
return false;
}
} else if (silent || is_script_executing()) {
Expand Down Expand Up @@ -138,7 +149,7 @@ open_editor(const char *file, unsigned int lineno)
if (lineno && opt_editor_line_number && string_format(lineno_cmd, "+%u", lineno))
editor_argv[argc++] = lineno_cmd;
editor_argv[argc] = file;
if (!open_external_viewer(editor_argv, repo.cdup, false, false, false, true, EDITOR_LINENO_MSG))
if (!open_external_viewer(editor_argv, repo.cdup, false, false, false, 0, true, EDITOR_LINENO_MSG))
opt_editor_line_number = false;
}

Expand Down
31 changes: 27 additions & 4 deletions src/keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ static size_t run_requests;

DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8)

#define COMMAND_FLAGS ":!?@<+"
#define COMMAND_FLAGS ":!?@<>+"

enum status_code
parse_run_request_flags(struct run_request_flags *flags, const char **argv)
Expand All @@ -474,6 +474,21 @@ parse_run_request_flags(struct run_request_flags *flags, const char **argv)
flags->exit = 1;
} else if (*argv[0] == '+') {
flags->echo = 1;
} else if (at_register_flag_open(argv[0])
&& at_register_escd_pair(argv[0] + 2)
&& at_register_flag_close(argv[0] + 4)) {
flags->register_key = argv[0][3];
argv[0] += 5;
continue;
} else if (at_register_flag_open(argv[0])
&& argv[0][2] >= REGISTER_KEY_MIN
&& argv[0][2] <= REGISTER_KEY_MAX
&& at_register_flag_close(argv[0] + 3)) {
flags->register_key = argv[0][2];
argv[0] += 4;
continue;
} else if (at_register_flag_open(argv[0])) {
return error("Invalid register flag");
} else if (*argv[0] != '!') {
break;
}
Expand Down Expand Up @@ -518,7 +533,7 @@ get_run_request(enum request request)
const char *
format_run_request_flags(const struct run_request *req)
{
static char flags[8];
static char flags[16];
int flagspos = 0;

memset(flags, 0, sizeof(flags));
Expand All @@ -529,13 +544,21 @@ format_run_request_flags(const struct run_request *req)
flags[flagspos] = '!'; /* Optional, if other flags are defined */

if (req->flags.silent)
flags[flagspos++] = '@';
flags[flagspos++] = '@';
if (req->flags.confirm)
flags[flagspos++] = '?';
flags[flagspos++] = '?';
if (req->flags.exit)
flags[flagspos++] = '<';
if (req->flags.echo)
flags[flagspos++] = '+';
if (req->flags.register_key) {
flags[flagspos++] = REGISTER_FLAG_OPEN_STR[0];
flags[flagspos++] = REGISTER_FLAG_OPEN_STR[1];
if (is_register_meta_char(req->flags.register_key))
flags[flagspos++] = REGISTER_ESC_CHAR;
flags[flagspos++] = req->flags.register_key;
flags[flagspos++] = REGISTER_FLAG_CLOSE_STR[0];
}
if (flagspos > 1)
flags[flagspos++] = 0;

Expand Down
Loading

0 comments on commit a07220c

Please sign in to comment.