Blob


1 /*
2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <limits.h>
18 #include <stdlib.h>
19 #include <string.h>
21 #include "compl.h"
22 #include "defaults.h"
23 #include "minibuffer.h"
24 #include "telescope.h"
25 #include "ui.h"
26 #include "utf8.h"
28 /* return 1 if moved, 0 otherwise */
29 static inline int
30 forward_line(struct buffer *buffer, int n)
31 {
32 struct vline *vl;
33 int did;
35 if (buffer->current_line == NULL)
36 return 0;
37 vl = buffer->current_line;
39 did = 0;
40 while (n != 0) {
41 if (n > 0) {
42 vl = TAILQ_NEXT(vl, vlines);
43 if (vl == NULL)
44 return did;
45 if (vl->parent->flags & L_HIDDEN)
46 continue;
47 buffer->current_line = vl;
48 n--;
49 } else {
50 vl = TAILQ_PREV(vl, vhead, vlines);
51 if (vl == NULL)
52 return did;
53 if (vl->parent->flags & L_HIDDEN)
54 continue;
55 if (buffer->current_line == buffer->top_line)
56 buffer->top_line = vl;
57 buffer->current_line = vl;
58 n++;
59 }
61 did = 1;
62 }
64 return did;
65 }
67 void
68 cmd_previous_line(struct buffer *buffer)
69 {
70 forward_line(buffer, -1);
71 }
73 void
74 cmd_next_line(struct buffer *buffer)
75 {
76 forward_line(buffer, +1);
77 }
79 void
80 cmd_backward_char(struct buffer *buffer)
81 {
82 if (buffer->cpoff != 0)
83 buffer->cpoff--;
84 }
86 void
87 cmd_forward_char(struct buffer *buffer)
88 {
89 size_t len = 0;
91 if (buffer->current_line->line != NULL)
92 len = utf8_cplen(buffer->current_line->line);
93 if (++buffer->cpoff > len)
94 buffer->cpoff = len;
95 }
97 void
98 cmd_backward_paragraph(struct buffer *buffer)
99 {
100 do {
101 if (!forward_line(buffer, -1)) {
102 message("No previous paragraph");
103 return;
105 } while (buffer->current_line->line != NULL ||
106 buffer->current_line->parent->type != LINE_TEXT);
109 void
110 cmd_forward_paragraph(struct buffer *buffer)
112 do {
113 if (!forward_line(buffer, +1)) {
114 message("No next paragraph");
115 return;
117 } while (buffer->current_line->line != NULL ||
118 buffer->current_line->parent->type != LINE_TEXT);
121 void
122 cmd_move_beginning_of_line(struct buffer *buffer)
124 buffer->cpoff = 0;
127 void
128 cmd_move_end_of_line(struct buffer *buffer)
130 struct vline *vl;
132 vl = buffer->current_line;
133 if (vl->line == NULL)
134 return;
135 buffer->cpoff = utf8_cplen(vl->line);
138 void
139 cmd_redraw(struct buffer *buffer)
141 ui_schedule_redraw();
144 void
145 cmd_scroll_line_up(struct buffer *buffer)
147 struct vline *vl;
149 if ((vl = TAILQ_PREV(buffer->top_line, vhead, vlines))
150 == NULL)
151 return;
153 forward_line(buffer, -1);
155 buffer->line_off--;
156 buffer->top_line = vl;
159 void
160 cmd_scroll_line_down(struct buffer *buffer)
162 if (!forward_line(buffer, +1))
163 return;
165 buffer->top_line = TAILQ_NEXT(buffer->top_line, vlines);
166 buffer->line_off++;
169 void
170 cmd_scroll_up(struct buffer *buffer)
172 forward_line(buffer, -1*body_lines);
175 void
176 cmd_scroll_down(struct buffer *buffer)
178 forward_line(buffer, body_lines);
181 void
182 cmd_beginning_of_buffer(struct buffer *buffer)
184 buffer->current_line = TAILQ_FIRST(&buffer->head);
185 buffer->cpoff = 0;
186 buffer->top_line = buffer->current_line;
187 buffer->line_off = 0;
190 void
191 cmd_end_of_buffer(struct buffer *buffer)
193 buffer->current_line = TAILQ_LAST(&buffer->head, vhead);
194 buffer->cpoff = body_cols;
197 void
198 cmd_kill_telescope(struct buffer *buffer)
200 save_session();
201 event_loopbreak();
204 void
205 cmd_push_button(struct buffer *buffer)
207 struct vline *vl;
208 struct line *l;
210 vl = buffer->current_line;
211 switch (vl->parent->type) {
212 case LINE_LINK:
213 load_url_in_tab(current_tab(), vl->parent->alt);
214 break;
215 case LINE_PRE_START:
216 l = TAILQ_NEXT(vl->parent, lines);
217 for (; l != NULL; l = TAILQ_NEXT(l, lines)) {
218 if (l->type == LINE_PRE_END)
219 break;
220 l->flags ^= L_HIDDEN;
222 break;
223 default:
224 break;
228 void
229 cmd_push_button_new_tab(struct buffer *buffer)
231 struct vline *vl;
233 vl = buffer->current_line;
234 if (vl->parent->type != LINE_LINK)
235 return;
237 new_tab(vl->parent->alt);
240 void
241 cmd_previous_button(struct buffer *buffer)
243 struct excursion place;
245 save_excursion(&place, buffer);
247 do {
248 if (!forward_line(buffer, -1)) {
249 restore_excursion(&place, buffer);
250 message("No previous link");
251 return;
253 } while (buffer->current_line->parent->type != LINE_LINK);
256 void
257 cmd_next_button(struct buffer *buffer)
259 struct excursion place;
261 save_excursion(&place, buffer);
263 do {
264 if (!forward_line(buffer, +1)){
265 restore_excursion(&place, buffer);
266 message("No next link");
267 return;
269 } while (buffer->current_line->parent->type != LINE_LINK);
272 static inline int
273 is_heading(const struct line *l)
275 return l->type == LINE_TITLE_1 ||
276 l->type == LINE_TITLE_2 ||
277 l->type == LINE_TITLE_3;
280 void
281 cmd_previous_heading(struct buffer *buffer)
283 struct excursion place;
285 save_excursion(&place, buffer);
287 do {
288 if (!forward_line(buffer, -1)) {
289 restore_excursion(&place, buffer);
290 message("No previous heading");
291 return;
293 } while (!is_heading(buffer->current_line->parent));
296 void
297 cmd_next_heading(struct buffer *buffer)
299 struct excursion place;
301 save_excursion(&place, buffer);
303 do {
304 if (!forward_line(buffer, +1)) {
305 restore_excursion(&place, buffer);
306 message("No next heading");
307 return;
309 } while (!is_heading(buffer->current_line->parent));
312 void
313 cmd_previous_page(struct buffer *buffer)
315 struct tab *tab = current_tab();
317 if (!load_previous_page(tab))
318 message("No previous page");
319 else
320 start_loading_anim(tab);
323 void
324 cmd_next_page(struct buffer *buffer)
326 struct tab *tab = current_tab();
328 if (!load_next_page(tab))
329 message("No next page");
330 else
331 start_loading_anim(tab);
334 void
335 cmd_clear_minibuf(struct buffer *buffer)
337 message(NULL);
340 void
341 cmd_execute_extended_command(struct buffer *buffer)
343 size_t len;
345 if (in_minibuffer) {
346 message("We don't have enable-recursive-minibuffers");
347 return;
350 enter_minibuffer(eecmd_self_insert, eecmd_select, exit_minibuffer,
351 &eecmd_history, compl_eecmd, NULL);
353 len = sizeof(ministate.prompt);
354 strlcpy(ministate.prompt, "", len);
356 if (thiskey.meta)
357 strlcat(ministate.prompt, "M-", len);
359 strlcat(ministate.prompt, ui_keyname(thiskey.key), len);
361 if (thiskey.meta)
362 strlcat(ministate.prompt, " ", len);
365 void
366 cmd_tab_close(struct buffer *buffer)
368 struct tab *tab, *t;
370 tab = current_tab();
371 if (TAILQ_PREV(tab, tabshead, tabs) == NULL &&
372 TAILQ_NEXT(tab, tabs) == NULL) {
373 message("Can't close the only tab.");
374 return;
377 if (evtimer_pending(&tab->loadingev, NULL))
378 evtimer_del(&tab->loadingev);
380 stop_tab(tab);
382 if ((t = TAILQ_PREV(tab, tabshead, tabs)) == NULL)
383 t = TAILQ_NEXT(tab, tabs);
384 TAILQ_REMOVE(&tabshead, tab, tabs);
385 free(tab);
387 switch_to_tab(t);
390 void
391 cmd_tab_close_other(struct buffer *buffer)
393 struct tab *t, *i;
395 TAILQ_FOREACH_SAFE(t, &tabshead, tabs, i) {
396 if (t->flags & TAB_CURRENT)
397 continue;
399 stop_tab(t);
400 TAILQ_REMOVE(&tabshead, t, tabs);
401 free(t);
405 void
406 cmd_tab_new(struct buffer *buffer)
408 const char *url;
410 if ((url = new_tab_url) == NULL)
411 url = NEW_TAB_URL;
413 new_tab(url);
416 void
417 cmd_tab_next(struct buffer *buffer)
419 struct tab *tab, *t;
421 tab = current_tab();
422 tab->flags &= ~TAB_CURRENT;
424 if ((t = TAILQ_NEXT(tab, tabs)) == NULL)
425 t = TAILQ_FIRST(&tabshead);
426 t->flags |= TAB_CURRENT;
427 t->flags &= ~TAB_URGENT;
430 void
431 cmd_tab_previous(struct buffer *buffer)
433 struct tab *tab, *t;
435 tab = current_tab();
436 tab->flags &= ~TAB_CURRENT;
438 if ((t = TAILQ_PREV(tab, tabshead, tabs)) == NULL)
439 t = TAILQ_LAST(&tabshead, tabshead);
440 t->flags |= TAB_CURRENT;
441 t->flags &= ~TAB_URGENT;
444 void
445 cmd_tab_move(struct buffer *buffer)
447 struct tab *tab, *t;
449 tab = current_tab();
450 t = TAILQ_NEXT(tab, tabs);
451 TAILQ_REMOVE(&tabshead, tab, tabs);
453 if (t == NULL)
454 TAILQ_INSERT_HEAD(&tabshead, tab, tabs);
455 else
456 TAILQ_INSERT_AFTER(&tabshead, t, tab, tabs);
459 void
460 cmd_tab_move_to(struct buffer *buffer)
462 struct tab *tab, *t;
464 tab = current_tab();
465 t = TAILQ_PREV(tab, tabshead, tabs);
466 TAILQ_REMOVE(&tabshead, tab, tabs);
468 if (t == NULL) {
469 if (TAILQ_EMPTY(&tabshead))
470 TAILQ_INSERT_HEAD(&tabshead, tab, tabs);
471 else
472 TAILQ_INSERT_TAIL(&tabshead, tab, tabs);
473 } else
474 TAILQ_INSERT_BEFORE(t, tab, tabs);
477 void
478 cmd_load_url(struct buffer *buffer)
480 if (in_minibuffer) {
481 message("We don't have enable-recursive-minibuffers");
482 return;
485 enter_minibuffer(lu_self_insert, lu_select, exit_minibuffer,
486 &lu_history, NULL, NULL);
487 strlcpy(ministate.prompt, "Load URL: ", sizeof(ministate.prompt));
488 strlcpy(ministate.buf, "gemini://", sizeof(ministate.buf));
489 cmd_move_end_of_line(&ministate.buffer);
492 void
493 cmd_load_current_url(struct buffer *buffer)
495 struct tab *tab = current_tab();
497 if (in_minibuffer) {
498 message("We don't have enable-recursive-minibuffers");
499 return;
502 enter_minibuffer(lu_self_insert, lu_select, exit_minibuffer,
503 &lu_history, NULL, NULL);
504 strlcpy(ministate.prompt, "Load URL: ", sizeof(ministate.prompt));
505 strlcpy(ministate.buf, tab->hist_cur->h, sizeof(ministate.buf));
506 ministate.buffer.cpoff = utf8_cplen(ministate.buf);
509 void
510 cmd_bookmark_page(struct buffer *buffer)
512 struct tab *tab = current_tab();
514 enter_minibuffer(lu_self_insert, bp_select, exit_minibuffer, NULL,
515 NULL, NULL);
516 strlcpy(ministate.prompt, "Bookmark URL: ", sizeof(ministate.prompt));
517 strlcpy(ministate.buf, tab->hist_cur->h, sizeof(ministate.buf));
518 ministate.buffer.cpoff = utf8_cplen(ministate.buf);
521 void
522 cmd_list_bookmarks(struct buffer *buffer)
524 load_url_in_tab(current_tab(), "about:bookmarks");
527 void
528 cmd_toggle_help(struct buffer *buffer)
530 ui_toggle_side_window();
533 void
534 cmd_inc_fill_column(struct buffer *buffer)
536 if (fill_column == INT_MAX)
537 return;
539 fill_column += 2;
540 message("fill-column: %d", fill_column);
542 ui_schedule_redraw();
545 void
546 cmd_dec_fill_column(struct buffer *buffer)
548 if (fill_column == INT_MAX || fill_column < 8)
549 return;
551 fill_column -= 2;
552 message("fill-column: %d", fill_column);
554 ui_schedule_redraw();
557 void
558 cmd_olivetti_mode(struct buffer *buffer)
560 olivetti_mode = !olivetti_mode;
561 if (olivetti_mode)
562 message("olivetti-mode enabled");
563 else
564 message("olivetti-mode disabled");
566 ui_schedule_redraw();
569 void
570 cmd_mini_delete_char(struct buffer *buffer)
572 char *c, *n;
574 if (!in_minibuffer) {
575 message("text is read-only");
576 return;
579 minibuffer_taint_hist();
581 c = utf8_nth(buffer->current_line->line, buffer->cpoff);
582 if (*c == '\0')
583 return;
584 n = utf8_next_cp(c);
586 memmove(c, n, strlen(n)+1);
588 recompute_completions(0);
591 void
592 cmd_mini_delete_backward_char(struct buffer *buffer)
594 char *c, *p, *start;
596 if (!in_minibuffer) {
597 message("text is read-only");
598 return;
601 minibuffer_taint_hist();
603 c = utf8_nth(buffer->current_line->line, buffer->cpoff);
604 start = buffer->current_line->line;
605 if (c == start)
606 return;
607 p = utf8_prev_cp(c-1, start);
609 memmove(p, c, strlen(c)+1);
610 buffer->cpoff--;
612 recompute_completions(0);
615 void
616 cmd_mini_kill_line(struct buffer *buffer)
618 char *c;
620 if (!in_minibuffer) {
621 message("text is read-only");
622 return;
625 minibuffer_taint_hist();
626 c = utf8_nth(buffer->current_line->line, buffer->cpoff);
627 *c = '\0';
629 recompute_completions(0);
632 void
633 cmd_mini_abort(struct buffer *buffer)
635 if (!in_minibuffer)
636 return;
638 ministate.abortfn();
641 void
642 cmd_mini_complete_and_exit(struct buffer *buffer)
644 if (!in_minibuffer)
645 return;
647 minibuffer_taint_hist();
648 ministate.donefn();
651 void
652 cmd_mini_previous_history_element(struct buffer *buffer)
654 if (ministate.history == NULL) {
655 message("No history");
656 return;
659 if (ministate.hist_cur == NULL ||
660 (ministate.hist_cur = TAILQ_PREV(ministate.hist_cur, mhisthead, entries)) == NULL) {
661 ministate.hist_cur = TAILQ_LAST(&ministate.history->head, mhisthead);
662 ministate.hist_off = ministate.history->len - 1;
663 if (ministate.hist_cur == NULL)
664 message("No prev item");
665 } else {
666 ministate.hist_off--;
669 if (ministate.hist_cur != NULL)
670 buffer->current_line->line = ministate.hist_cur->h;
673 void
674 cmd_mini_next_history_element(struct buffer *buffer)
676 if (ministate.history == NULL) {
677 message("No history");
678 return;
681 if (ministate.hist_cur == NULL ||
682 (ministate.hist_cur = TAILQ_NEXT(ministate.hist_cur, entries)) == NULL) {
683 ministate.hist_cur = TAILQ_FIRST(&ministate.history->head);
684 ministate.hist_off = 0;
685 if (ministate.hist_cur == NULL)
686 message("No next item");
687 } else {
688 ministate.hist_off++;
691 if (ministate.hist_cur != NULL)
692 buffer->current_line->line = ministate.hist_cur->h;