Skip to content

Commit d2fd448

Browse files
committed
implement user-programmable registers
* %(register:r) expansion variable * set-register r value * !=(r)echo arbitrary shell command
1 parent e08aebf commit d2fd448

File tree

15 files changed

+482
-20
lines changed

15 files changed

+482
-20
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ TIG_OBJS = \
312312
src/grep.o \
313313
src/ui.o \
314314
src/apps.o \
315+
src/registers.o \
315316
$(GRAPH_OBJS) \
316317
$(COMPAT_OBJS)
317318

doc/manual.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ following variables.
194194
|%(repo:is-inside-work-tree)
195195
|Whether Tig is running inside a work tree,
196196
either `true` or `false`.
197+
|%(register:x) |A user-defined register value, where `x` is an ASCII
198+
character.
197199
|=============================================================================
198200

199201
Example user-defined commands:
@@ -489,7 +491,8 @@ Prompt
489491
|:script <file> |Execute commands from `<file>`.
490492
|:exec <flags><args...> |Execute command using `<args>` with external
491493
user-defined command option flags defined in `<flags>`.
492-
|:echo <args...> |Display text in the status bar.
494+
|:echo <args...> |Display text in the status bar.
495+
|:set-register <char> <args...> |Load a value into the register named `<char>`.
493496
|=============================================================================
494497

495498
[[external-commands]]

doc/tigrc.5.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,8 @@ the command that should be executed.
665665
|< |Exit Tig after executing the command.
666666
|> |Re-open Tig instantly in the last displayed view after
667667
executing the command.
668+
|=(x) |Run the command synchronously, and store the first
669+
line of output in the register named `x`.
668670
|=============================================================================
669671

670672
Unless otherwise specified, commands are run in the foreground with their
@@ -717,6 +719,8 @@ following variable names, which are substituted before commands are run:
717719
|%(repo:is-inside-work-tree)
718720
|Whether Tig is running inside a work tree,
719721
either `true` or `false`.
722+
|%(register:x) |A user-defined register value, where `x` is an ASCII
723+
character.
720724
|=============================================================================
721725

722726
Examples:

include/tig/argv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define TIG_ARGV_H
1616

1717
#include "tig/tig.h"
18+
#include "tig/registers.h"
1819

1920
/*
2021
* Argument array helpers.
@@ -62,6 +63,7 @@ typedef unsigned long argv_number;
6263
struct argv_env {
6364
ARGV_ENV_INFO(ARGV_ENV_FIELDS)
6465
unsigned long goto_lineno;
66+
char *registers[SIZEOF_REGISTERS];
6567
char search[SIZEOF_STR];
6668
char none[1];
6769
};

include/tig/display.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ bool save_view(struct view *view, const char *path);
5353
bool vertical_split_is_enabled(enum vertical_split vsplit, int height, int width);
5454
int apply_vertical_split(int base_width);
5555

56-
bool open_external_viewer(const char *argv[], const char *dir, bool silent, bool confirm, bool echo, bool quick, bool refresh, const char *notice);
56+
bool open_external_viewer(const char *argv[], const char *dir, bool silent, bool confirm, bool echo, bool quick, char register_key, bool refresh, const char *notice);
5757
void open_editor(const char *file, unsigned int lineno);
5858
void enable_mouse(bool enable);
5959

include/tig/keys.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ struct run_request_flags {
8484
bool internal;
8585
bool echo;
8686
bool quick;
87+
char register_key;
8788
};
8889

8990
struct run_request {

include/tig/registers.h

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/* Copyright (c) 2006-2017 Jonas Fonseca <[email protected]>
2+
*
3+
* This program is free software; you can redistribute it and/or
4+
* modify it under the terms of the GNU General Public License as
5+
* published by the Free Software Foundation; either version 2 of
6+
* the License, or (at your option) any later version.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*/
13+
14+
#ifndef TIG_REGISTERS_H
15+
#define TIG_REGISTERS_H
16+
17+
#include "tig/types.h"
18+
19+
/* Index 0 of the register array, corresponding to character key ASCII
20+
* space, is kept empty and reserved for internal use as "no register".
21+
*/
22+
#define REGISTER_KEY_MIN '!' /* corresponding to index 1 */
23+
#define REGISTER_KEY_MAX '~' /* corresponding to index 94 */
24+
#define REGISTER_KEY_OFFSET 0x20
25+
#define SIZEOF_REGISTERS 1 + REGISTER_KEY_MAX - REGISTER_KEY_OFFSET
26+
27+
#define REGISTER_FLAG_OPEN_STR "=("
28+
#define REGISTER_FLAG_CLOSE_STR ")"
29+
#define REGISTER_ESC_CHAR '\\'
30+
31+
#define is_register_esc_char(ch) \
32+
((ch) == REGISTER_ESC_CHAR)
33+
34+
#define is_register_meta_char(ch) \
35+
(is_register_esc_char(ch) \
36+
|| ((ch) == REGISTER_FLAG_OPEN_STR[1]) \
37+
|| ((ch) == REGISTER_FLAG_CLOSE_STR[0]) \
38+
|| ((ch) == '"') \
39+
|| ((ch) == '\''))
40+
41+
#define at_register_flag_open(p) \
42+
(((p)[0] == REGISTER_FLAG_OPEN_STR[0]) && ((p)[1] == REGISTER_FLAG_OPEN_STR[1]))
43+
44+
#define at_register_flag_close(p) \
45+
((p)[0] == REGISTER_FLAG_CLOSE_STR[0])
46+
47+
#define at_register_escd_pair(p) \
48+
(is_register_esc_char((p)[0]) && is_register_meta_char((p)[1]))
49+
50+
#define register_key_to_index(key) \
51+
((((key) >= REGISTER_KEY_MIN) && ((key) <= REGISTER_KEY_MAX)) ? (unsigned int) (key) - REGISTER_KEY_OFFSET : 0)
52+
53+
bool register_set(const char key, const char *value);
54+
const char *register_get(const char key);
55+
56+
/* metacharacters occur twice, once as an escaped sequence */
57+
#define REGISTER_INFO(_) \
58+
_("\\\\", '\\') \
59+
_("\\(", '(') \
60+
_("\\)", ')') \
61+
_("\\\"", '"') \
62+
_("\\'", '\'') \
63+
_("!", '!') \
64+
_("\"", '"') \
65+
_("#", '#') \
66+
_("$", '$') \
67+
_("%", '%') \
68+
_("&", '&') \
69+
_("'", '\'') \
70+
_("(", '(') \
71+
_(")", ')') \
72+
_("*", '*') \
73+
_("+", '+') \
74+
_(",", ',') \
75+
_("-", '-') \
76+
_(".", '.') \
77+
_("/", '/') \
78+
_("0", '0') \
79+
_("1", '1') \
80+
_("2", '2') \
81+
_("3", '3') \
82+
_("4", '4') \
83+
_("5", '5') \
84+
_("6", '6') \
85+
_("7", '7') \
86+
_("8", '8') \
87+
_("9", '9') \
88+
_(":", ':') \
89+
_(";", ';') \
90+
_("<", '<') \
91+
_("=", '=') \
92+
_(">", '>') \
93+
_("?", '?') \
94+
_("@", '@') \
95+
_("A", 'A') \
96+
_("B", 'B') \
97+
_("C", 'C') \
98+
_("D", 'D') \
99+
_("E", 'E') \
100+
_("F", 'F') \
101+
_("G", 'G') \
102+
_("H", 'H') \
103+
_("I", 'I') \
104+
_("J", 'J') \
105+
_("K", 'K') \
106+
_("L", 'L') \
107+
_("M", 'M') \
108+
_("N", 'N') \
109+
_("O", 'O') \
110+
_("P", 'P') \
111+
_("Q", 'Q') \
112+
_("R", 'R') \
113+
_("S", 'S') \
114+
_("T", 'T') \
115+
_("U", 'U') \
116+
_("V", 'V') \
117+
_("W", 'W') \
118+
_("X", 'X') \
119+
_("Y", 'Y') \
120+
_("Z", 'Z') \
121+
_("[", '[') \
122+
_("\\", '\\') \
123+
_("]", ']') \
124+
_("^", '^') \
125+
_("_", '_') \
126+
_("`", '`') \
127+
_("a", 'a') \
128+
_("b", 'b') \
129+
_("c", 'c') \
130+
_("d", 'd') \
131+
_("e", 'e') \
132+
_("f", 'f') \
133+
_("g", 'g') \
134+
_("h", 'h') \
135+
_("i", 'i') \
136+
_("j", 'j') \
137+
_("k", 'k') \
138+
_("l", 'l') \
139+
_("m", 'm') \
140+
_("n", 'n') \
141+
_("o", 'o') \
142+
_("p", 'p') \
143+
_("q", 'q') \
144+
_("r", 'r') \
145+
_("s", 's') \
146+
_("t", 't') \
147+
_("u", 'u') \
148+
_("v", 'v') \
149+
_("w", 'w') \
150+
_("x", 'x') \
151+
_("y", 'y') \
152+
_("z", 'z') \
153+
_("{", '{') \
154+
_("|", '|') \
155+
_("}", '}') \
156+
_("~", '~')
157+
158+
#endif
159+
/* vim: set ts=8 sw=8 noexpandtab: */

src/argv.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "tig/repo.h"
1717
#include "tig/options.h"
1818
#include "tig/prompt.h"
19+
#include "tig/registers.h"
1920

2021
static bool
2122
concat_argv(const char *argv[], char *buf, size_t buflen, const char *sep, bool quoted)
@@ -376,6 +377,12 @@ format_append_arg(struct format_context *format, const char ***dst_argv, const c
376377
while (arg) {
377378
const char *var = strstr(arg, "%(");
378379
const char *closing = var ? strchr(var, ')') : NULL;
380+
381+
/* todo hardcoding is robust and compact but ugly */
382+
if (var &&
383+
(!strcmp(var, "%(register:\\))") || !strcmp(var, "%(register:))")))
384+
closing++;
385+
379386
const char *next = closing ? closing + 1 : NULL;
380387
const int len = var ? var - arg : strlen(arg);
381388

@@ -468,6 +475,10 @@ argv_format(struct argv_env *argv_env, const char ***dst_argv, const char *src_a
468475
#define FORMAT_REPO_VAR(type, name) \
469476
{ "%(repo:" #name ")", STRING_SIZE("%(repo:" #name ")"), type ## _formatter, &repo.name, "" },
470477
REPO_INFO(FORMAT_REPO_VAR)
478+
#define FORMAT_REGISTER_VAR(namestr, keychar) \
479+
{ "%(register:" namestr ")", STRING_SIZE("%(register:" namestr ")"), argv_string_formatter, \
480+
argv_env->registers[ MIN(keychar,REGISTER_KEY_MAX) - REGISTER_KEY_OFFSET ], "" },
481+
REGISTER_INFO(FORMAT_REGISTER_VAR)
471482
};
472483
struct format_context format = { vars, ARRAY_SIZE(vars), "", 0, file_filter };
473484
int argc;

src/display.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "tig/draw.h"
2121
#include "tig/display.h"
2222
#include "tig/watch.h"
23+
#include "tig/registers.h"
2324

2425
static void set_terminal_modes(void);
2526

@@ -61,19 +62,29 @@ open_script(const char *path)
6162
}
6263

6364
bool
64-
open_external_viewer(const char *argv[], const char *dir, bool silent, bool confirm, bool echo, bool quick, bool do_refresh, const char *notice)
65+
open_external_viewer(const char *argv[], const char *dir, bool silent, bool confirm, bool echo, bool quick, char register_key, bool do_refresh, const char *notice)
6566
{
6667
bool ok;
6768

68-
if (echo) {
69+
if (echo || register_key) {
6970
char buf[SIZEOF_STR] = "";
7071

7172
io_run_buf(argv, buf, sizeof(buf), dir, false);
7273
if (*buf) {
73-
report("%s", buf);
74+
if (register_key)
75+
register_set(register_key, buf);
76+
if (echo)
77+
report("%s", buf);
78+
else
79+
report_clear();
7480
return true;
7581
} else {
76-
report("No output");
82+
if (register_key)
83+
register_set(register_key, "");
84+
if (echo)
85+
report("No output");
86+
else
87+
report_clear();
7788
return false;
7889
}
7990
} else if (silent || is_script_executing()) {
@@ -148,7 +159,7 @@ open_editor(const char *file, unsigned int lineno)
148159
if (lineno && opt_editor_line_number && string_format(lineno_cmd, "+%u", lineno))
149160
editor_argv[argc++] = lineno_cmd;
150161
editor_argv[argc] = file;
151-
if (!open_external_viewer(editor_argv, repo.cdup, false, false, false, false, true, EDITOR_LINENO_MSG))
162+
if (!open_external_viewer(editor_argv, repo.cdup, false, false, false, false, 0, true, EDITOR_LINENO_MSG))
152163
opt_editor_line_number = false;
153164
}
154165

src/keys.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ static size_t run_requests;
449449

450450
DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8)
451451

452-
#define COMMAND_FLAGS ":!?@<+>"
452+
#define COMMAND_FLAGS ":!?@<+>="
453453

454454
enum status_code
455455
parse_run_request_flags(struct run_request_flags *flags, const char **argv)
@@ -475,6 +475,21 @@ parse_run_request_flags(struct run_request_flags *flags, const char **argv)
475475
flags->echo = 1;
476476
} else if (*argv[0] == '>') {
477477
flags->quick = 1;
478+
} else if (at_register_flag_open(argv[0])
479+
&& at_register_escd_pair(argv[0] + 2)
480+
&& at_register_flag_close(argv[0] + 4)) {
481+
flags->register_key = argv[0][3];
482+
argv[0] += 5;
483+
continue;
484+
} else if (at_register_flag_open(argv[0])
485+
&& argv[0][2] >= REGISTER_KEY_MIN
486+
&& argv[0][2] <= REGISTER_KEY_MAX
487+
&& at_register_flag_close(argv[0] + 3)) {
488+
flags->register_key = argv[0][2];
489+
argv[0] += 4;
490+
continue;
491+
} else if (at_register_flag_open(argv[0])) {
492+
return error("Invalid register flag");
478493
} else if (*argv[0] != '!') {
479494
break;
480495
}
@@ -519,7 +534,7 @@ get_run_request(enum request request)
519534
const char *
520535
format_run_request_flags(const struct run_request *req)
521536
{
522-
static char flags[8];
537+
static char flags[16];
523538
int flagspos = 0;
524539

525540
memset(flags, 0, sizeof(flags));
@@ -530,15 +545,23 @@ format_run_request_flags(const struct run_request *req)
530545
flags[flagspos] = '!'; /* Optional, if other flags are defined */
531546

532547
if (req->flags.silent)
533-
flags[flagspos++] = '@';
548+
flags[flagspos++] = '@';
534549
if (req->flags.confirm)
535-
flags[flagspos++] = '?';
550+
flags[flagspos++] = '?';
536551
if (req->flags.exit)
537552
flags[flagspos++] = '<';
538553
if (req->flags.echo)
539554
flags[flagspos++] = '+';
540555
if (req->flags.quick)
541556
flags[flagspos++] = '>';
557+
if (req->flags.register_key) {
558+
flags[flagspos++] = REGISTER_FLAG_OPEN_STR[0];
559+
flags[flagspos++] = REGISTER_FLAG_OPEN_STR[1];
560+
if (is_register_meta_char(req->flags.register_key))
561+
flags[flagspos++] = REGISTER_ESC_CHAR;
562+
flags[flagspos++] = req->flags.register_key;
563+
flags[flagspos++] = REGISTER_FLAG_CLOSE_STR[0];
564+
}
542565
if (flagspos > 1)
543566
flags[flagspos++] = 0;
544567

0 commit comments

Comments
 (0)