Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <stddef.h> | ||
2 | #include <string.h> | ||
3 | #include <unistd.h> | ||
4 | #include "vars.h" | ||
5 | #include "editor.h" | ||
6 | #include "selection.h" | ||
7 | #include "util/array.h" | ||
8 | #include "util/bsearch.h" | ||
9 | #include "util/numtostr.h" | ||
10 | #include "util/path.h" | ||
11 | #include "util/str-util.h" | ||
12 | #include "util/xmalloc.h" | ||
13 | #include "view.h" | ||
14 | |||
15 | typedef struct { | ||
16 | char name[12]; | ||
17 | char *(*expand)(const EditorState *e); | ||
18 | } BuiltinVar; | ||
19 | |||
20 | 2 | static char *expand_dte_home(const EditorState *e) | |
21 | { | ||
22 | 2 | return xstrdup(e->user_config_dir); | |
23 | } | ||
24 | |||
25 | 1 | static char *expand_file(const EditorState *e) | |
26 | { | ||
27 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1 | if (!e->buffer || !e->buffer->abs_filename) { |
28 | return NULL; | ||
29 | } | ||
30 | ✗ | return xstrdup(e->buffer->abs_filename); | |
31 | } | ||
32 | |||
33 | 1 | static char *expand_file_dir(const EditorState *e) | |
34 | { | ||
35 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1 | if (!e->buffer || !e->buffer->abs_filename) { |
36 | return NULL; | ||
37 | } | ||
38 | ✗ | return path_dirname(e->buffer->abs_filename); | |
39 | } | ||
40 | |||
41 | 2 | static char *expand_rfile(const EditorState *e) | |
42 | { | ||
43 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
2 | if (!e->buffer || !e->buffer->abs_filename) { |
44 | return NULL; | ||
45 | } | ||
46 | ✗ | char buf[8192]; | |
47 | ✗ | const char *cwd = getcwd(buf, sizeof buf); | |
48 | ✗ | const char *abs = e->buffer->abs_filename; | |
49 | ✗ | return likely(cwd) ? path_relative(abs, cwd) : xstrdup(abs); | |
50 | } | ||
51 | |||
52 | 1 | static char *expand_rfiledir(const EditorState *e) | |
53 | { | ||
54 | 1 | char *rfile = expand_rfile(e); | |
55 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!rfile) { |
56 | return NULL; | ||
57 | } | ||
58 | |||
59 | ✗ | StringView dir = path_slice_dirname(rfile); | |
60 | ✗ | if (rfile != (char*)dir.data) { | |
61 | // rfile contains no directory part (i.e. no slashes), so there's | ||
62 | // nothing to "slice" and we simply return "." instead | ||
63 | ✗ | free(rfile); | |
64 | ✗ | return xstrdup("."); | |
65 | } | ||
66 | |||
67 | // Overwrite the last slash in rfile with a null terminator, so as | ||
68 | // to remove the last path component (the filename) | ||
69 | ✗ | rfile[dir.length] = '\0'; | |
70 | ✗ | return rfile; | |
71 | } | ||
72 | |||
73 | 1 | static char *expand_filetype(const EditorState *e) | |
74 | { | ||
75 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | return e->buffer ? xstrdup(e->buffer->options.filetype) : NULL; |
76 | } | ||
77 | |||
78 | 1 | static char *expand_colno(const EditorState *e) | |
79 | { | ||
80 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | return e->view ? xstrdup(umax_to_str(e->view->cx_display + 1)) : NULL; |
81 | } | ||
82 | |||
83 | 1 | static char *expand_lineno(const EditorState *e) | |
84 | { | ||
85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | return e->view ? xstrdup(umax_to_str(e->view->cy + 1)) : NULL; |
86 | } | ||
87 | |||
88 | 1 | static char *expand_msgpos(const EditorState *e) | |
89 | { | ||
90 | 1 | return xstrdup(umax_to_str(e->messages.pos + 1)); | |
91 | } | ||
92 | |||
93 | 1 | static char *expand_word(const EditorState *e) | |
94 | { | ||
95 | 1 | const View *view = e->view; | |
96 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (unlikely(!view)) { |
97 | return NULL; | ||
98 | } | ||
99 | |||
100 | ✗ | if (view->selection) { | |
101 | ✗ | SelectionInfo info = init_selection(view); | |
102 | ✗ | size_t len = info.eo - info.so; | |
103 | ✗ | if (unlikely(len == 0)) { | |
104 | return NULL; | ||
105 | } | ||
106 | ✗ | char *selection = block_iter_get_bytes(&info.si, len); | |
107 | ✗ | BUG_ON(!selection); | |
108 | ✗ | selection[len] = '\0'; // See comment in block_iter_get_bytes() | |
109 | ✗ | return selection; | |
110 | } | ||
111 | |||
112 | ✗ | StringView word = view_get_word_under_cursor(view); | |
113 | ✗ | return word.length ? xstrcut(word.data, word.length) : NULL; | |
114 | } | ||
115 | |||
116 | static const BuiltinVar normal_vars[] = { | ||
117 | {"COLNO", expand_colno}, | ||
118 | {"DTE_HOME", expand_dte_home}, | ||
119 | {"FILE", expand_file}, | ||
120 | {"FILEDIR", expand_file_dir}, | ||
121 | {"FILETYPE", expand_filetype}, | ||
122 | {"LINENO", expand_lineno}, | ||
123 | {"MSGPOS", expand_msgpos}, | ||
124 | {"RFILE", expand_rfile}, | ||
125 | {"RFILEDIR", expand_rfiledir}, | ||
126 | {"WORD", expand_word}, | ||
127 | }; | ||
128 | |||
129 | 18 | UNITTEST { | |
130 | 18 | CHECK_BSEARCH_ARRAY(normal_vars, name, strcmp); | |
131 | 18 | } | |
132 | |||
133 | 19 | char *expand_normal_var(const EditorState *e, const char *name) | |
134 | { | ||
135 | 19 | const BuiltinVar *var = BSEARCH(name, normal_vars, vstrcmp); | |
136 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 8 times.
|
19 | if (var) { |
137 | 11 | return var->expand(e); | |
138 | } | ||
139 | 8 | const char *str = xgetenv(name); | |
140 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
|
8 | return str ? xstrdup(str) : NULL; |
141 | } | ||
142 | |||
143 | 1 | void collect_normal_vars(PointerArray *a, const char *prefix) | |
144 | { | ||
145 | 1 | COLLECT_STRING_FIELDS(normal_vars, name, a, prefix); | |
146 | 1 | } | |
147 |