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 }