vis: prefix enum VisMotion values with VIS_
[vis.git] / vis-modes.c
blob6e2e196d5ca3e8cb68c1ff6e04987ddc67fe6803
1 #include "vis-core.h"
2 #include "util.h"
4 Mode *mode_get(Vis *vis, enum VisMode mode) {
5 if (mode < LENGTH(vis_modes))
6 return &vis_modes[mode];
7 return NULL;
10 void mode_set(Vis *vis, Mode *new_mode) {
11 if (vis->mode == new_mode)
12 return;
13 if (vis->mode->leave)
14 vis->mode->leave(vis, new_mode);
15 if (vis->mode->isuser)
16 vis->mode_prev = vis->mode;
17 vis->mode = new_mode;
18 if (new_mode->enter)
19 new_mode->enter(vis, vis->mode_prev);
20 vis->win->ui->draw_status(vis->win->ui);
23 static bool mode_map(Mode *mode, const char *key, KeyBinding *binding) {
24 return map_put(mode->bindings, key, binding);
27 bool vis_mode_map(Vis *vis, enum VisMode modeid, const char *key, KeyBinding *binding) {
28 Mode *mode = mode_get(vis, modeid);
29 return mode && mode_map(mode, key, binding);
32 bool vis_mode_bindings(Vis *vis, enum VisMode modeid, KeyBinding **bindings) {
33 Mode *mode = mode_get(vis, modeid);
34 if (!mode)
35 return false;
36 bool success = true;
37 for (KeyBinding *kb = *bindings; kb->key; kb++) {
38 if (!mode_map(mode, kb->key, kb))
39 success = false;
41 return success;
44 bool vis_mode_unmap(Vis *vis, enum VisMode modeid, const char *key) {
45 Mode *mode = mode_get(vis, modeid);
46 return mode && map_delete(mode->bindings, key);
49 /** mode switching event handlers */
51 static void vis_mode_operator_enter(Vis *vis, Mode *old) {
52 vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_OPERATOR_OPTION];
55 static void vis_mode_operator_leave(Vis *vis, Mode *new) {
56 vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
59 static void vis_mode_operator_input(Vis *vis, const char *str, size_t len) {
60 /* invalid operator */
61 vis_cancel(vis);
62 mode_set(vis, vis->mode_prev);
65 static void vis_mode_visual_enter(Vis *vis, Mode *old) {
66 if (!old->visual) {
67 for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
68 view_cursors_selection_start(c);
69 vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_TEXTOBJ];
73 static void vis_mode_visual_line_enter(Vis *vis, Mode *old) {
74 if (!old->visual) {
75 for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
76 view_cursors_selection_start(c);
77 vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_TEXTOBJ];
79 vis_motion(vis, VIS_MOVE_LINE_END);
82 static void vis_mode_visual_line_leave(Vis *vis, Mode *new) {
83 if (!new->visual) {
84 view_selections_clear(vis->win->view);
85 vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
86 } else {
87 view_cursor_to(vis->win->view, view_cursor_get(vis->win->view));
91 static void vis_mode_visual_leave(Vis *vis, Mode *new) {
92 if (!new->visual) {
93 view_selections_clear(vis->win->view);
94 vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
98 static void vis_mode_prompt_input(Vis *vis, const char *str, size_t len) {
99 vis_insert_key(vis, str, len);
102 static void vis_mode_prompt_enter(Vis *vis, Mode *old) {
103 if (old->isuser && old != &vis_modes[VIS_MODE_PROMPT])
104 vis->mode_before_prompt = old;
107 static void vis_mode_prompt_leave(Vis *vis, Mode *new) {
108 if (new->isuser)
109 vis_prompt_hide(vis);
112 static void vis_mode_insert_enter(Vis *vis, Mode *old) {
113 if (!vis->macro_operator) {
114 macro_operator_record(vis);
115 action_reset(&vis->action_prev);
116 vis->action_prev.macro = vis->macro_operator;
117 vis->action_prev.op = &ops[VIS_OP_INSERT];
121 static void vis_mode_insert_leave(Vis *vis, Mode *new) {
122 /* make sure we can recover the current state after an editing operation */
123 text_snapshot(vis->win->file->text);
124 if (new == mode_get(vis, VIS_MODE_NORMAL))
125 macro_operator_stop(vis);
128 static void vis_mode_insert_idle(Vis *vis) {
129 text_snapshot(vis->win->file->text);
132 static void vis_mode_insert_input(Vis *vis, const char *str, size_t len) {
133 vis_insert_key(vis, str, len);
136 static void vis_mode_replace_enter(Vis *vis, Mode *old) {
137 if (!vis->macro_operator) {
138 macro_operator_record(vis);
139 action_reset(&vis->action_prev);
140 vis->action_prev.macro = vis->macro_operator;
141 vis->action_prev.op = &ops[VIS_OP_REPLACE];
145 static void vis_mode_replace_leave(Vis *vis, Mode *new) {
146 /* make sure we can recover the current state after an editing operation */
147 text_snapshot(vis->win->file->text);
148 if (new == mode_get(vis, VIS_MODE_NORMAL))
149 macro_operator_stop(vis);
152 static void vis_mode_replace_input(Vis *vis, const char *str, size_t len) {
153 vis_replace_key(vis, str, len);
159 * the tree of modes currently looks like this. the double line between OPERATOR-OPTION
160 * and OPERATOR is only in effect once an operator is detected. that is when entering the
161 * OPERATOR mode its parent is set to OPERATOR-OPTION which makes {INNER-,}TEXTOBJ
162 * reachable. once the operator is processed (i.e. the OPERATOR mode is left) its parent
163 * mode is reset back to MOVE.
165 * Similarly the +-ed line between OPERATOR and TEXTOBJ is only active within the visual
166 * modes.
169 * BASIC
170 * (arrow keys etc.)
171 * / |
172 * /-------------------/ |
173 * READLINE MOVE
174 * / \ (h,j,k,l ...)
175 * / \ | \-----------------\
176 * / \ | |
177 * INSERT PROMPT OPERATOR ++++ INNER-TEXTOBJ
178 * | (history etc) (d,c,y,p ..) + (i [wsp[]()b<>{}B"'`] )
179 * | | \\ + |
180 * | | \\ + |
181 * REPLACE NORMAL \\ + TEXTOBJ
182 * | \\ + (a [wsp[]()b<>{}B"'`] )
183 * | \\ + + |
184 * | \\ + + |
185 * VISUAL \\ OPERATOR-OPTION
186 * | \\ (v,V)
187 * | \\ //
188 * | \\======//
189 * VISUAL-LINE
192 Mode vis_modes[] = {
193 [VIS_MODE_BASIC] = {
194 .name = "BASIC",
195 .parent = NULL,
197 [VIS_MODE_MOVE] = {
198 .name = "MOVE",
199 .parent = &vis_modes[VIS_MODE_BASIC],
201 [VIS_MODE_TEXTOBJ] = {
202 .name = "TEXT-OBJECTS",
203 .parent = &vis_modes[VIS_MODE_MOVE],
205 [VIS_MODE_OPERATOR_OPTION] = {
206 .name = "OPERATOR-OPTION",
207 .parent = &vis_modes[VIS_MODE_TEXTOBJ],
209 [VIS_MODE_OPERATOR] = {
210 .name = "OPERATOR",
211 .parent = &vis_modes[VIS_MODE_MOVE],
212 .enter = vis_mode_operator_enter,
213 .leave = vis_mode_operator_leave,
214 .input = vis_mode_operator_input,
216 [VIS_MODE_NORMAL] = {
217 .name = "NORMAL",
218 .status = "",
219 .help = "",
220 .isuser = true,
221 .parent = &vis_modes[VIS_MODE_OPERATOR],
223 [VIS_MODE_VISUAL] = {
224 .name = "VISUAL",
225 .status = "--VISUAL--",
226 .help = "",
227 .isuser = true,
228 .parent = &vis_modes[VIS_MODE_OPERATOR],
229 .enter = vis_mode_visual_enter,
230 .leave = vis_mode_visual_leave,
231 .visual = true,
233 [VIS_MODE_VISUAL_LINE] = {
234 .name = "VISUAL LINE",
235 .status = "--VISUAL LINE--",
236 .help = "",
237 .isuser = true,
238 .parent = &vis_modes[VIS_MODE_VISUAL],
239 .enter = vis_mode_visual_line_enter,
240 .leave = vis_mode_visual_line_leave,
241 .visual = true,
243 [VIS_MODE_READLINE] = {
244 .name = "READLINE",
245 .parent = &vis_modes[VIS_MODE_BASIC],
247 [VIS_MODE_PROMPT] = {
248 .name = "PROMPT",
249 .help = "",
250 .isuser = true,
251 .parent = &vis_modes[VIS_MODE_READLINE],
252 .input = vis_mode_prompt_input,
253 .enter = vis_mode_prompt_enter,
254 .leave = vis_mode_prompt_leave,
256 [VIS_MODE_INSERT] = {
257 .name = "INSERT",
258 .status = "--INSERT--",
259 .help = "",
260 .isuser = true,
261 .parent = &vis_modes[VIS_MODE_READLINE],
262 .enter = vis_mode_insert_enter,
263 .leave = vis_mode_insert_leave,
264 .input = vis_mode_insert_input,
265 .idle = vis_mode_insert_idle,
266 .idle_timeout = 3,
268 [VIS_MODE_REPLACE] = {
269 .name = "REPLACE",
270 .status = "--REPLACE--",
271 .help = "",
272 .isuser = true,
273 .parent = &vis_modes[VIS_MODE_INSERT],
274 .enter = vis_mode_replace_enter,
275 .leave = vis_mode_replace_leave,
276 .input = vis_mode_replace_input,
277 .idle = vis_mode_insert_idle,
278 .idle_timeout = 3,