vis: use <Tab> to align multiple cursors in normal mode
[vis.git] / vis-prompt.c
blob42b1a2c2134462218b5491da5c2e7fad55bdc171
1 #include <string.h>
2 #include "vis-core.h"
3 #include "text-objects.h"
4 #include "text-util.h"
6 bool vis_prompt_cmd(Vis *vis, const char *cmd) {
7 if (!cmd || !cmd[0] || !cmd[1])
8 return true;
9 switch (cmd[0]) {
10 case '/':
11 return vis_motion(vis, VIS_MOVE_SEARCH_FORWARD, cmd+1);
12 case '?':
13 return vis_motion(vis, VIS_MOVE_SEARCH_BACKWARD, cmd+1);
14 case '+':
15 case ':':
17 bool ret = vis_cmd(vis, cmd+1);
18 if (ret && vis->mode->visual)
19 vis_mode_switch(vis, VIS_MODE_NORMAL);
20 return ret;
22 default:
23 return false;
27 static void prompt_hide(Win *win) {
28 Text *txt = win->file->text;
29 size_t size = text_size(txt);
30 /* make sure that file is new line terminated */
31 char lastchar;
32 if (size > 1 && text_byte_get(txt, size-1, &lastchar) && lastchar != '\n')
33 text_insert(txt, size, "\n", 1);
34 /* remove empty entries */
35 Filerange line = text_object_line(txt, size);
36 size_t line_size = text_range_size(&line);
37 if (line_size <= 2)
38 text_delete(txt, line.start, line_size);
39 vis_window_close(win);
42 static void prompt_restore(Win *win) {
43 Vis *vis = win->vis;
44 /* restore window and mode which was active before the prompt window
45 * we deliberately don't use vis_mode_switch because we do not want
46 * to invoke the modes enter/leave functions */
47 if (win->parent)
48 vis->win = win->parent;
49 vis->mode = win->parent_mode;
52 static const char *prompt_enter(Vis *vis, const char *keys, const Arg *arg) {
53 Win *prompt = vis->win;
54 View *view = prompt->view;
55 Text *txt = prompt->file->text;
56 Win *win = prompt->parent;
57 char *cmd = NULL;
59 Filerange range = view_selection_get(view);
60 if (!text_range_valid(&range))
61 range = text_object_line(txt, view_cursor_get(view));
62 if (text_range_valid(&range))
63 cmd = text_bytes_alloc0(txt, range.start, text_range_size(&range));
65 if (!win || !cmd) {
66 vis_info_show(vis, "Prompt window invalid\n");
67 prompt_restore(prompt);
68 prompt_hide(prompt);
69 free(cmd);
70 return keys;
73 size_t len = strlen(cmd);
74 if (len > 0 && cmd[len-1] == '\n')
75 cmd[len-1] = '\0';
77 bool lastline = (range.end == text_size(txt));
79 prompt_restore(prompt);
80 if (vis_prompt_cmd(vis, cmd)) {
81 prompt_hide(prompt);
82 if (!lastline) {
83 text_delete(txt, range.start, text_range_size(&range));
84 text_appendf(txt, "%s\n", cmd);
86 } else {
87 vis->win = prompt;
88 vis->mode = &vis_modes[VIS_MODE_INSERT];
90 free(cmd);
91 vis_draw(vis);
92 return keys;
95 static const char *prompt_esc(Vis *vis, const char *keys, const Arg *arg) {
96 Win *prompt = vis->win;
97 if (view_cursors_count(prompt->view) > 1) {
98 view_cursors_clear(prompt->view);
99 } else {
100 prompt_restore(prompt);
101 prompt_hide(prompt);
103 return keys;
106 static const char *prompt_up(Vis *vis, const char *keys, const Arg *arg) {
107 vis_motion(vis, VIS_MOVE_LINE_UP);
108 vis_window_mode_unmap(vis->win, VIS_MODE_INSERT, "<Up>");
109 view_options_set(vis->win->view, UI_OPTION_NONE);
110 return keys;
113 static const char *prompt_backspace(Vis *vis, const char *keys, const Arg *arg) {
114 Win *prompt = vis->win;
115 Text *txt = prompt->file->text;
116 size_t size = text_size(txt);
117 size_t pos = view_cursor_get(prompt->view);
118 char c;
119 if (pos == size && (pos == 1 || (size >= 2 && text_byte_get(txt, size-2, &c) && c == '\n'))) {
120 prompt_restore(prompt);
121 prompt_hide(prompt);
122 } else {
123 vis_operator(vis, VIS_OP_DELETE);
124 vis_motion(vis, VIS_MOVE_CHAR_PREV);
126 return keys;
129 static const KeyBinding prompt_enter_binding = {
130 .key = "<Enter>",
131 .action = &(KeyAction){
132 .func = prompt_enter,
136 static const KeyBinding prompt_esc_binding = {
137 .key = "<Escape>",
138 .action = &(KeyAction){
139 .func = prompt_esc,
143 static const KeyBinding prompt_up_binding = {
144 .key = "<Up>",
145 .action = &(KeyAction){
146 .func = prompt_up,
150 static const KeyBinding prompt_backspace_binding = {
151 .key = "<Enter>",
152 .action = &(KeyAction){
153 .func = prompt_backspace,
157 void vis_prompt_show(Vis *vis, const char *title) {
158 Win *active = vis->win;
159 Win *prompt = window_new_file(vis, title[0] == ':' ? vis->command_file : vis->search_file);
160 if (!prompt)
161 return;
162 if (vis->mode->visual)
163 window_selection_save(active);
164 view_options_set(prompt->view, UI_OPTION_ONELINE);
165 Text *txt = prompt->file->text;
166 text_insert(txt, text_size(txt), title, strlen(title));
167 view_cursors_scroll_to(view_cursor(prompt->view), text_size(txt));
168 view_draw(prompt->view);
169 prompt->parent = active;
170 prompt->parent_mode = vis->mode;
171 vis_window_mode_map(prompt, VIS_MODE_NORMAL, "<Enter>", &prompt_enter_binding);
172 vis_window_mode_map(prompt, VIS_MODE_INSERT, "<Enter>", &prompt_enter_binding);
173 vis_window_mode_map(prompt, VIS_MODE_VISUAL, "<Enter>", &prompt_enter_binding);
174 vis_window_mode_map(prompt, VIS_MODE_NORMAL, "<Escape>", &prompt_esc_binding);
175 vis_window_mode_map(prompt, VIS_MODE_INSERT, "<Up>", &prompt_up_binding);
176 vis_window_mode_map(prompt, VIS_MODE_INSERT, "<Backspace>", &prompt_backspace_binding);
177 vis_mode_switch(vis, VIS_MODE_INSERT);
178 vis_draw(vis);
181 void vis_info_show(Vis *vis, const char *msg, ...) {
182 va_list ap;
183 va_start(ap, msg);
184 vis->ui->info(vis->ui, msg, ap);
185 va_end(ap);
188 void vis_info_hide(Vis *vis) {
189 vis->ui->info_hide(vis->ui);
192 void vis_message_show(Vis *vis, const char *msg) {
193 if (!msg)
194 return;
195 if (!vis->message_window) {
196 if (!vis_window_new(vis, NULL))
197 return;
198 vis->message_window = vis->win;
201 Win *win = vis->message_window;
202 Text *txt = win->file->text;
203 size_t pos = text_size(txt);
204 text_appendf(txt, "%s\n", msg);
205 text_save(txt, NULL);
206 view_cursor_to(win->view, pos);
209 void vis_message_hide(Vis *vis) {
210 if (!vis->message_window)
211 return;
212 vis_window_close(vis->message_window);
213 vis->message_window = NULL;