vis: implement :set cursorline
[vis.git] / vis-modes.c
blobea539f1479543f54cf3d71f9ff90b4c55decd2ac
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;
105 /* prompt manipulations e.g. <Backspace> should not affect default register */
106 Register tmp = vis->registers[VIS_REG_PROMPT];
107 vis->registers[VIS_REG_PROMPT] = vis->registers[VIS_REG_DEFAULT];
108 vis->registers[VIS_REG_DEFAULT] = tmp;
112 static void vis_mode_prompt_leave(Vis *vis, Mode *new) {
113 if (new->isuser) {
114 vis_prompt_hide(vis);
115 Register tmp = vis->registers[VIS_REG_DEFAULT];
116 vis->registers[VIS_REG_DEFAULT] = vis->registers[VIS_REG_PROMPT];
117 vis->registers[VIS_REG_PROMPT] = tmp;
121 static void vis_mode_insert_enter(Vis *vis, Mode *old) {
122 if (!vis->macro_operator) {
123 macro_operator_record(vis);
124 action_reset(&vis->action_prev);
125 vis->action_prev.macro = vis->macro_operator;
126 vis->action_prev.op = &ops[VIS_OP_INSERT];
130 static void vis_mode_insert_leave(Vis *vis, Mode *new) {
131 /* make sure we can recover the current state after an editing operation */
132 text_snapshot(vis->win->file->text);
133 if (new == mode_get(vis, VIS_MODE_NORMAL))
134 macro_operator_stop(vis);
137 static void vis_mode_insert_idle(Vis *vis) {
138 text_snapshot(vis->win->file->text);
141 static void vis_mode_insert_input(Vis *vis, const char *str, size_t len) {
142 vis_insert_key(vis, str, len);
145 static void vis_mode_replace_enter(Vis *vis, Mode *old) {
146 if (!vis->macro_operator) {
147 macro_operator_record(vis);
148 action_reset(&vis->action_prev);
149 vis->action_prev.macro = vis->macro_operator;
150 vis->action_prev.op = &ops[VIS_OP_REPLACE];
154 static void vis_mode_replace_leave(Vis *vis, Mode *new) {
155 /* make sure we can recover the current state after an editing operation */
156 text_snapshot(vis->win->file->text);
157 if (new == mode_get(vis, VIS_MODE_NORMAL))
158 macro_operator_stop(vis);
161 static void vis_mode_replace_input(Vis *vis, const char *str, size_t len) {
162 vis_replace_key(vis, str, len);
168 * the tree of modes currently looks like this. the double line between OPERATOR-OPTION
169 * and OPERATOR is only in effect once an operator is detected. that is when entering the
170 * OPERATOR mode its parent is set to OPERATOR-OPTION which makes {INNER-,}TEXTOBJ
171 * reachable. once the operator is processed (i.e. the OPERATOR mode is left) its parent
172 * mode is reset back to MOVE.
174 * Similarly the +-ed line between OPERATOR and TEXTOBJ is only active within the visual
175 * modes.
178 * BASIC
179 * (arrow keys etc.)
180 * / |
181 * /-------------------/ |
182 * READLINE MOVE
183 * / \ (h,j,k,l ...)
184 * / \ | \-----------------\
185 * / \ | |
186 * INSERT PROMPT OPERATOR ++++ INNER-TEXTOBJ
187 * | (history etc) (d,c,y,p ..) + (i [wsp[]()b<>{}B"'`] )
188 * | | \\ + |
189 * | | \\ + |
190 * REPLACE NORMAL \\ + TEXTOBJ
191 * | \\ + (a [wsp[]()b<>{}B"'`] )
192 * | \\ + + |
193 * | \\ + + |
194 * VISUAL \\ OPERATOR-OPTION
195 * | \\ (v,V)
196 * | \\ //
197 * | \\======//
198 * VISUAL-LINE
201 Mode vis_modes[] = {
202 [VIS_MODE_BASIC] = {
203 .name = "BASIC",
204 .parent = NULL,
206 [VIS_MODE_MOVE] = {
207 .name = "MOVE",
208 .parent = &vis_modes[VIS_MODE_BASIC],
210 [VIS_MODE_TEXTOBJ] = {
211 .name = "TEXT-OBJECTS",
212 .parent = &vis_modes[VIS_MODE_MOVE],
214 [VIS_MODE_OPERATOR_OPTION] = {
215 .name = "OPERATOR-OPTION",
216 .parent = &vis_modes[VIS_MODE_TEXTOBJ],
218 [VIS_MODE_OPERATOR] = {
219 .name = "OPERATOR",
220 .parent = &vis_modes[VIS_MODE_MOVE],
221 .enter = vis_mode_operator_enter,
222 .leave = vis_mode_operator_leave,
223 .input = vis_mode_operator_input,
225 [VIS_MODE_NORMAL] = {
226 .name = "NORMAL",
227 .status = "",
228 .help = "",
229 .isuser = true,
230 .parent = &vis_modes[VIS_MODE_OPERATOR],
232 [VIS_MODE_VISUAL] = {
233 .name = "VISUAL",
234 .status = "--VISUAL--",
235 .help = "",
236 .isuser = true,
237 .parent = &vis_modes[VIS_MODE_OPERATOR],
238 .enter = vis_mode_visual_enter,
239 .leave = vis_mode_visual_leave,
240 .visual = true,
242 [VIS_MODE_VISUAL_LINE] = {
243 .name = "VISUAL LINE",
244 .status = "--VISUAL LINE--",
245 .help = "",
246 .isuser = true,
247 .parent = &vis_modes[VIS_MODE_VISUAL],
248 .enter = vis_mode_visual_line_enter,
249 .leave = vis_mode_visual_line_leave,
250 .visual = true,
252 [VIS_MODE_READLINE] = {
253 .name = "READLINE",
254 .parent = &vis_modes[VIS_MODE_BASIC],
256 [VIS_MODE_PROMPT] = {
257 .name = "PROMPT",
258 .help = "",
259 .isuser = true,
260 .parent = &vis_modes[VIS_MODE_READLINE],
261 .input = vis_mode_prompt_input,
262 .enter = vis_mode_prompt_enter,
263 .leave = vis_mode_prompt_leave,
265 [VIS_MODE_INSERT] = {
266 .name = "INSERT",
267 .status = "--INSERT--",
268 .help = "",
269 .isuser = true,
270 .parent = &vis_modes[VIS_MODE_READLINE],
271 .enter = vis_mode_insert_enter,
272 .leave = vis_mode_insert_leave,
273 .input = vis_mode_insert_input,
274 .idle = vis_mode_insert_idle,
275 .idle_timeout = 3,
277 [VIS_MODE_REPLACE] = {
278 .name = "REPLACE",
279 .status = "--REPLACE--",
280 .help = "",
281 .isuser = true,
282 .parent = &vis_modes[VIS_MODE_INSERT],
283 .enter = vis_mode_replace_enter,
284 .leave = vis_mode_replace_leave,
285 .input = vis_mode_replace_input,
286 .idle = vis_mode_insert_idle,
287 .idle_timeout = 3,