vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
vis-prompt.c
(5883B)
1 #include <string.h>
2 #include "vis-core.h"
3 #include "text-motions.h"
4 #include "text-objects.h"
5 #include "text-util.h"
6
7 bool vis_prompt_cmd(Vis *vis, const char *cmd) {
8 if (!cmd || !cmd[0] || !cmd[1])
9 return true;
10 switch (cmd[0]) {
11 case '/':
12 return vis_motion(vis, VIS_MOVE_SEARCH_FORWARD, cmd+1);
13 case '?':
14 return vis_motion(vis, VIS_MOVE_SEARCH_BACKWARD, cmd+1);
15 case '+':
16 case ':':
17 register_put0(vis, &vis->registers[VIS_REG_COMMAND], cmd+1);
18 return vis_cmd(vis, cmd+1);
19 default:
20 return false;
21 }
22 }
23
24 static void prompt_hide(Win *win) {
25 Text *txt = win->file->text;
26 size_t size = text_size(txt);
27 /* make sure that file is new line terminated */
28 char lastchar = '\0';
29 if (size >= 1 && text_byte_get(txt, size-1, &lastchar) && lastchar != '\n')
30 text_insert(txt, size, "\n", 1);
31 /* remove empty entries */
32 Filerange line_range = text_object_line(txt, text_size(txt)-1);
33 char *line = text_bytes_alloc0(txt, line_range.start, text_range_size(&line_range));
34 if (line && (line[0] == '\n' || (strchr(":/?", line[0]) && (line[1] == '\n' || line[1] == '\0'))))
35 text_delete_range(txt, &line_range);
36 free(line);
37 vis_window_close(win);
38 }
39
40 static void prompt_restore(Win *win) {
41 Vis *vis = win->vis;
42 /* restore window and mode which was active before the prompt window
43 * we deliberately don't use vis_mode_switch because we do not want
44 * to invoke the modes enter/leave functions */
45 if (win->parent)
46 vis->win = win->parent;
47 vis->mode = win->parent_mode;
48 }
49
50 static const char *prompt_enter(Vis *vis, const char *keys, const Arg *arg) {
51 Win *prompt = vis->win;
52 View *view = &prompt->view;
53 Text *txt = prompt->file->text;
54 Win *win = prompt->parent;
55 char *cmd = NULL;
56
57 Filerange range = view_selections_get(view->selection);
58 if (!vis->mode->visual) {
59 const char *pattern = NULL;
60 Regex *regex = text_regex_new();
61 size_t pos = view_cursor_get(view);
62 if (prompt->file == vis->command_file)
63 pattern = "^:";
64 else if (prompt->file == vis->search_file)
65 pattern = "^(/|\\?)";
66 int cflags = REG_EXTENDED|REG_NEWLINE|(REG_ICASE*vis->ignorecase);
67 if (pattern && regex && text_regex_compile(regex, pattern, cflags) == 0) {
68 size_t end = text_line_end(txt, pos);
69 size_t prev = text_search_backward(txt, end, regex);
70 if (prev > pos)
71 prev = EPOS;
72 size_t next = text_search_forward(txt, pos, regex);
73 if (next < pos)
74 next = text_size(txt);
75 range = text_range_new(prev, next);
76 }
77 text_regex_free(regex);
78 }
79 if (text_range_valid(&range))
80 cmd = text_bytes_alloc0(txt, range.start, text_range_size(&range));
81
82 if (!win || !cmd) {
83 if (!win)
84 vis_info_show(vis, "Prompt window invalid");
85 else if (!cmd)
86 vis_info_show(vis, "Failed to detect command");
87 prompt_restore(prompt);
88 prompt_hide(prompt);
89 free(cmd);
90 return keys;
91 }
92
93 size_t len = strlen(cmd);
94 if (len > 0 && cmd[len-1] == '\n')
95 cmd[len-1] = '\0';
96
97 bool lastline = (range.end == text_size(txt));
98
99 prompt_restore(prompt);
100 if (vis_prompt_cmd(vis, cmd)) {
101 prompt_hide(prompt);
102 if (!lastline) {
103 text_delete(txt, range.start, text_range_size(&range));
104 text_appendf(txt, "%s\n", cmd);
105 }
106 } else {
107 vis->win = prompt;
108 vis->mode = &vis_modes[VIS_MODE_INSERT];
109 }
110 free(cmd);
111 vis_draw(vis);
112 return keys;
113 }
114
115 static const char *prompt_esc(Vis *vis, const char *keys, const Arg *arg) {
116 Win *prompt = vis->win;
117 if (prompt->view.selection_count > 1) {
118 view_selections_dispose_all(&prompt->view);
119 } else {
120 prompt_restore(prompt);
121 prompt_hide(prompt);
122 }
123 return keys;
124 }
125
126 static const char *prompt_up(Vis *vis, const char *keys, const Arg *arg) {
127 vis_motion(vis, VIS_MOVE_LINE_UP);
128 vis_window_mode_unmap(vis->win, VIS_MODE_INSERT, "<Up>");
129 win_options_set(vis->win, UI_OPTION_SYMBOL_EOF);
130 return keys;
131 }
132
133 static const KeyBinding prompt_enter_binding = {
134 .key = "<Enter>",
135 .action = &(KeyAction){
136 .func = prompt_enter,
137 },
138 };
139
140 static const KeyBinding prompt_esc_binding = {
141 .key = "<Escape>",
142 .action = &(KeyAction){
143 .func = prompt_esc,
144 },
145 };
146
147 static const KeyBinding prompt_up_binding = {
148 .key = "<Up>",
149 .action = &(KeyAction){
150 .func = prompt_up,
151 },
152 };
153
154 static const KeyBinding prompt_tab_binding = {
155 .key = "<Tab>",
156 .alias = "<C-x><C-o>",
157 };
158
159 void vis_prompt_show(Vis *vis, const char *title) {
160 Win *active = vis->win;
161 Win *prompt = window_new_file(vis, title[0] == ':' ? vis->command_file : vis->search_file,
162 UI_OPTION_ONELINE);
163 if (!prompt)
164 return;
165 Text *txt = prompt->file->text;
166 text_appendf(txt, "%s\n", title);
167 Selection *sel = view_selections_primary_get(&prompt->view);
168 view_cursors_scroll_to(sel, text_size(txt)-1);
169 prompt->parent = active;
170 prompt->parent_mode = vis->mode;
171 vis_window_mode_map(prompt, VIS_MODE_NORMAL, true, "<Enter>", &prompt_enter_binding);
172 vis_window_mode_map(prompt, VIS_MODE_INSERT, true, "<Enter>", &prompt_enter_binding);
173 vis_window_mode_map(prompt, VIS_MODE_INSERT, true, "<C-j>", &prompt_enter_binding);
174 vis_window_mode_map(prompt, VIS_MODE_VISUAL, true, "<Enter>", &prompt_enter_binding);
175 vis_window_mode_map(prompt, VIS_MODE_NORMAL, true, "<Escape>", &prompt_esc_binding);
176 vis_window_mode_map(prompt, VIS_MODE_INSERT, true, "<Up>", &prompt_up_binding);
177 if (CONFIG_LUA)
178 vis_window_mode_map(prompt, VIS_MODE_INSERT, true, "<Tab>", &prompt_tab_binding);
179 vis_mode_switch(vis, VIS_MODE_INSERT);
180 }
181
182 void vis_info_show(Vis *vis, const char *msg, ...) {
183 va_list ap;
184 va_start(ap, msg);
185 ui_info_show(&vis->ui, msg, ap);
186 va_end(ap);
187 }
188
189 void vis_message_show(Vis *vis, const char *msg) {
190 if (!msg)
191 return;
192 if (!vis->message_window)
193 vis->message_window = window_new_file(vis, vis->error_file, UI_OPTION_STATUSBAR);
194 Win *win = vis->message_window;
195 if (!win)
196 return;
197 Text *txt = win->file->text;
198 size_t pos = text_size(txt);
199 text_appendf(txt, "%s\n", msg);
200 text_save(txt, NULL);
201 view_cursors_to(win->view.selection, pos);
202 vis_window_focus(win);
203 }