4 #include "text-motions.h"
7 static void keyaction_free(KeyAction
*action
) {
10 free((char*)action
->name
);
11 free(VIS_HELP_USE((char*)action
->help
));
15 KeyAction
*vis_action_new(Vis
*vis
, const char *name
, const char *help
, KeyActionFunction
*func
, Arg arg
) {
16 KeyAction
*action
= calloc(1, sizeof *action
);
19 if (name
&& !(action
->name
= strdup(name
)))
22 if (help
&& !(action
->help
= strdup(help
)))
27 if (!array_add_ptr(&vis
->actions_user
, action
))
31 keyaction_free(action
);
35 void vis_action_free(Vis
*vis
, KeyAction
*action
) {
38 size_t len
= array_length(&vis
->actions_user
);
39 for (size_t i
= 0; i
< len
; i
++) {
40 if (action
== array_get_ptr(&vis
->actions_user
, i
)) {
41 keyaction_free(action
);
42 array_remove(&vis
->actions_user
, i
);
48 KeyBinding
*vis_binding_new(Vis
*vis
) {
49 KeyBinding
*binding
= calloc(1, sizeof *binding
);
50 if (binding
&& array_add_ptr(&vis
->bindings
, binding
))
56 void vis_binding_free(Vis
*vis
, KeyBinding
*binding
) {
59 size_t len
= array_length(&vis
->bindings
);
60 for (size_t i
= 0; i
< len
; i
++) {
61 if (binding
== array_get_ptr(&vis
->bindings
, i
)) {
63 free((char*)binding
->alias
);
64 if (binding
->action
&& !binding
->action
->name
)
65 vis_action_free(vis
, (KeyAction
*)binding
->action
);
67 array_remove(&vis
->bindings
, i
);
73 Mode
*mode_get(Vis
*vis
, enum VisMode mode
) {
74 if (mode
< LENGTH(vis_modes
))
75 return &vis_modes
[mode
];
79 void mode_set(Vis
*vis
, Mode
*new_mode
) {
80 if (vis
->mode
== new_mode
)
83 vis
->mode
->leave(vis
, new_mode
);
84 if (vis
->mode
!= &vis_modes
[VIS_MODE_OPERATOR_PENDING
])
85 vis
->mode_prev
= vis
->mode
;
88 new_mode
->enter(vis
, vis
->mode_prev
);
91 void vis_mode_switch(Vis
*vis
, enum VisMode mode
) {
92 if (mode
< LENGTH(vis_modes
))
93 mode_set(vis
, &vis_modes
[mode
]);
96 enum VisMode
vis_mode_from(Vis
*vis
, const char *name
) {
97 for (size_t i
= 0; name
&& i
< LENGTH(vis_modes
); i
++) {
98 Mode
*mode
= &vis_modes
[i
];
99 if (!strcasecmp(mode
->name
, name
))
102 return VIS_MODE_INVALID
;
105 enum VisMode
vis_mode_get(Vis
*vis
) {
106 return vis
->mode
->id
;
109 static bool mode_unmap(Mode
*mode
, const char *key
) {
110 return mode
&& mode
->bindings
&& map_delete(mode
->bindings
, key
);
113 bool vis_mode_unmap(Vis
*vis
, enum VisMode id
, const char *key
) {
114 return id
< LENGTH(vis_modes
) && mode_unmap(&vis_modes
[id
], key
);
117 bool vis_window_mode_unmap(Win
*win
, enum VisMode id
, const char *key
) {
118 return id
< LENGTH(win
->modes
) && mode_unmap(&win
->modes
[id
], key
);
121 static bool mode_map(Vis
*vis
, Mode
*mode
, bool force
, const char *key
, const KeyBinding
*binding
) {
124 if (binding
->alias
&& key
[0] != '<' && strncmp(key
, binding
->alias
, strlen(key
)) == 0)
126 if (!mode
->bindings
&& !(mode
->bindings
= map_new()))
129 map_delete(mode
->bindings
, key
);
130 return map_put(mode
->bindings
, key
, binding
);
133 bool vis_mode_map(Vis
*vis
, enum VisMode id
, bool force
, const char *key
, const KeyBinding
*binding
) {
134 return id
< LENGTH(vis_modes
) && mode_map(vis
, &vis_modes
[id
], force
, key
, binding
);
137 bool vis_window_mode_map(Win
*win
, enum VisMode id
, bool force
, const char *key
, const KeyBinding
*binding
) {
138 return id
< LENGTH(win
->modes
) && mode_map(win
->vis
, &win
->modes
[id
], force
, key
, binding
);
141 /** mode switching event handlers */
143 static void vis_mode_normal_enter(Vis
*vis
, Mode
*old
) {
144 if (old
!= mode_get(vis
, VIS_MODE_INSERT
) && old
!= mode_get(vis
, VIS_MODE_REPLACE
))
146 if (vis
->autoindent
&& strcmp(vis
->key_prev
, "<Enter>") == 0) {
147 Text
*txt
= vis
->win
->file
->text
;
148 for (Cursor
*c
= view_cursors(vis
->win
->view
); c
; c
= view_cursors_next(c
)) {
149 size_t pos
= view_cursors_pos(c
);
150 size_t start
= text_line_start(txt
, pos
);
151 size_t end
= text_line_end(txt
, pos
);
152 if (start
== pos
&& start
== end
) {
153 size_t begin
= text_line_begin(txt
, pos
);
154 size_t len
= start
- begin
;
156 text_delete(txt
, begin
, len
);
157 view_cursors_to(c
, pos
-len
);
162 macro_operator_stop(vis
);
163 if (!vis
->win
->parent
&& vis
->action_prev
.op
== &vis_operators
[VIS_OP_MODESWITCH
] &&
164 vis
->action_prev
.count
> 1) {
165 /* temporarily disable motion, in something like `5atext`
166 * we should only move the cursor once then insert the text */
167 const Movement
*motion
= vis
->action_prev
.movement
;
169 vis
->action_prev
.movement
= &vis_motions
[VIS_MOVE_NOP
];
170 /* we already inserted the text once, so temporarily decrease count */
171 vis
->action_prev
.count
--;
173 vis
->action_prev
.count
++;
174 vis
->action_prev
.movement
= motion
;
176 /* make sure we can recover the current state after an editing operation */
177 vis_file_snapshot(vis
, vis
->win
->file
);
180 static void vis_mode_operator_input(Vis
*vis
, const char *str
, size_t len
) {
181 /* invalid operator */
183 mode_set(vis
, vis
->mode_prev
);
186 static void vis_mode_visual_enter(Vis
*vis
, Mode
*old
) {
188 for (Cursor
*c
= view_cursors(vis
->win
->view
); c
; c
= view_cursors_next(c
))
189 view_cursors_selection_start(c
);
193 static void vis_mode_visual_line_enter(Vis
*vis
, Mode
*old
) {
195 for (Cursor
*c
= view_cursors(vis
->win
->view
); c
; c
= view_cursors_next(c
))
196 view_cursors_selection_start(c
);
199 vis_motion(vis
, VIS_MOVE_NOP
);
202 static void vis_mode_visual_line_leave(Vis
*vis
, Mode
*new) {
204 window_selection_save(vis
->win
);
205 view_selections_clear(vis
->win
->view
);
207 view_cursor_to(vis
->win
->view
, view_cursor_get(vis
->win
->view
));
211 static void vis_mode_visual_leave(Vis
*vis
, Mode
*new) {
213 window_selection_save(vis
->win
);
214 view_selections_clear(vis
->win
->view
);
218 static void vis_mode_insert_replace_enter(Vis
*vis
, Mode
*old
) {
219 if (vis
->win
->parent
)
221 if (!vis
->action
.op
) {
222 action_reset(&vis
->action_prev
);
223 vis
->action_prev
.op
= &vis_operators
[VIS_OP_MODESWITCH
];
224 vis
->action_prev
.mode
= vis
->mode
->id
;
226 macro_operator_record(vis
);
229 static void vis_mode_insert_idle(Vis
*vis
) {
230 vis_file_snapshot(vis
, vis
->win
->file
);
233 static void vis_mode_insert_input(Vis
*vis
, const char *str
, size_t len
) {
234 vis_insert_key(vis
, str
, len
);
237 static void vis_mode_replace_input(Vis
*vis
, const char *str
, size_t len
) {
238 vis_replace_key(vis
, str
, len
);
242 [VIS_MODE_OPERATOR_PENDING
] = {
243 .id
= VIS_MODE_OPERATOR_PENDING
,
244 .name
= "OPERATOR-PENDING",
245 .input
= vis_mode_operator_input
,
248 [VIS_MODE_NORMAL
] = {
249 .id
= VIS_MODE_NORMAL
,
252 .enter
= vis_mode_normal_enter
,
254 [VIS_MODE_VISUAL
] = {
255 .id
= VIS_MODE_VISUAL
,
259 .enter
= vis_mode_visual_enter
,
260 .leave
= vis_mode_visual_leave
,
263 [VIS_MODE_VISUAL_LINE
] = {
264 .id
= VIS_MODE_VISUAL_LINE
,
265 .name
= "VISUAL-LINE",
266 .parent
= &vis_modes
[VIS_MODE_VISUAL
],
267 .status
= "VISUAL-LINE",
269 .enter
= vis_mode_visual_line_enter
,
270 .leave
= vis_mode_visual_line_leave
,
273 [VIS_MODE_INSERT
] = {
274 .id
= VIS_MODE_INSERT
,
278 .enter
= vis_mode_insert_replace_enter
,
279 .input
= vis_mode_insert_input
,
280 .idle
= vis_mode_insert_idle
,
283 [VIS_MODE_REPLACE
] = {
284 .id
= VIS_MODE_REPLACE
,
286 .parent
= &vis_modes
[VIS_MODE_INSERT
],
289 .enter
= vis_mode_insert_replace_enter
,
290 .input
= vis_mode_replace_input
,
291 .idle
= vis_mode_insert_idle
,