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->meta.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->meta.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_tab_select(struct buffer *buffer)
480 if (in_minibuffer) {
481 message("We don't have enable-recursive-minibuffers");
482 return;
485 enter_minibuffer(minibuffer_self_insert, ts_select, exit_minibuffer,
486 NULL, compl_ts, NULL);
487 strlcpy(ministate.prompt, "Select tab: ", sizeof(ministate.prompt));
490 void
491 cmd_load_url(struct buffer *buffer)
493 if (in_minibuffer) {
494 message("We don't have enable-recursive-minibuffers");
495 return;
498 enter_minibuffer(lu_self_insert, lu_select, exit_minibuffer,
499 &lu_history, NULL, NULL);
500 strlcpy(ministate.prompt, "Load URL: ", sizeof(ministate.prompt));
501 strlcpy(ministate.buf, "gemini://", sizeof(ministate.buf));
502 cmd_move_end_of_line(&ministate.buffer);
505 void
506 cmd_load_current_url(struct buffer *buffer)
508 struct tab *tab = current_tab();
510 if (in_minibuffer) {
511 message("We don't have enable-recursive-minibuffers");
512 return;
515 enter_minibuffer(lu_self_insert, lu_select, exit_minibuffer,
516 &lu_history, NULL, NULL);
517 strlcpy(ministate.prompt, "Load URL: ", sizeof(ministate.prompt));
518 strlcpy(ministate.buf, tab->hist_cur->h, sizeof(ministate.buf));
519 ministate.buffer.cpoff = utf8_cplen(ministate.buf);
522 void
523 cmd_reload_page(struct buffer *buffer)
525 struct tab *tab;
527 tab = current_tab();
528 load_url_in_tab(tab, tab->hist_cur->h);
531 void
532 cmd_bookmark_page(struct buffer *buffer)
534 struct tab *tab = current_tab();
536 enter_minibuffer(lu_self_insert, bp_select, exit_minibuffer, NULL,
537 NULL, NULL);
538 strlcpy(ministate.prompt, "Bookmark URL: ", sizeof(ministate.prompt));
539 strlcpy(ministate.buf, tab->hist_cur->h, sizeof(ministate.buf));
540 ministate.buffer.cpoff = utf8_cplen(ministate.buf);
543 void
544 cmd_list_bookmarks(struct buffer *buffer)
546 load_url_in_tab(current_tab(), "about:bookmarks");
549 void
550 cmd_toggle_help(struct buffer *buffer)
552 ui_toggle_side_window();
555 void
556 cmd_link_select(struct buffer *buffer)
558 if (in_minibuffer) {
559 message("We don't have enable-recursive-minibuffers");
560 return;
563 enter_minibuffer(minibuffer_self_insert, ls_select, exit_minibuffer,
564 NULL, compl_ls, TAILQ_FIRST(&buffer->page.head));
565 strlcpy(ministate.prompt, "Select link: ", sizeof(ministate.prompt));
568 void
569 cmd_swiper(struct buffer *buffer)
571 if (in_minibuffer) {
572 message("We don't have enable-recursive-minibuffers");
573 return;
576 enter_minibuffer(minibuffer_self_insert, swiper_select, exit_minibuffer,
577 NULL, compl_swiper, TAILQ_FIRST(&buffer->page.head));
578 strlcpy(ministate.prompt, "Select line: ", sizeof(ministate.prompt));
581 void
582 cmd_inc_fill_column(struct buffer *buffer)
584 if (fill_column == INT_MAX)
585 return;
587 fill_column += 2;
588 message("fill-column: %d", fill_column);
590 ui_schedule_redraw();
593 void
594 cmd_dec_fill_column(struct buffer *buffer)
596 if (fill_column == INT_MAX || fill_column < 8)
597 return;
599 fill_column -= 2;
600 message("fill-column: %d", fill_column);
602 ui_schedule_redraw();
605 void
606 cmd_olivetti_mode(struct buffer *buffer)
608 olivetti_mode = !olivetti_mode;
609 if (olivetti_mode)
610 message("olivetti-mode enabled");
611 else
612 message("olivetti-mode disabled");
614 ui_schedule_redraw();
617 void
618 cmd_mini_delete_char(struct buffer *buffer)
620 char *c, *n;
622 if (!in_minibuffer) {
623 message("text is read-only");
624 return;
627 minibuffer_taint_hist();
629 c = utf8_nth(buffer->current_line->line, buffer->cpoff);
630 if (*c == '\0')
631 return;
632 n = utf8_next_cp(c);
634 memmove(c, n, strlen(n)+1);
636 recompute_completions(0);
639 void
640 cmd_mini_delete_backward_char(struct buffer *buffer)
642 char *c, *p, *start;
644 if (!in_minibuffer) {
645 message("text is read-only");
646 return;
649 minibuffer_taint_hist();
651 c = utf8_nth(buffer->current_line->line, buffer->cpoff);
652 start = buffer->current_line->line;
653 if (c == start)
654 return;
655 p = utf8_prev_cp(c-1, start);
657 memmove(p, c, strlen(c)+1);
658 buffer->cpoff--;
660 recompute_completions(0);
663 void
664 cmd_mini_kill_line(struct buffer *buffer)
666 char *c;
668 if (!in_minibuffer) {
669 message("text is read-only");
670 return;
673 minibuffer_taint_hist();
674 c = utf8_nth(buffer->current_line->line, buffer->cpoff);
675 *c = '\0';
677 recompute_completions(0);
680 void
681 cmd_mini_abort(struct buffer *buffer)
683 if (!in_minibuffer)
684 return;
686 ministate.abortfn();
689 void
690 cmd_mini_complete_and_exit(struct buffer *buffer)
692 if (!in_minibuffer)
693 return;
695 minibuffer_taint_hist();
696 ministate.donefn();
699 void
700 cmd_mini_previous_history_element(struct buffer *buffer)
702 if (ministate.history == NULL) {
703 message("No history");
704 return;
707 if (ministate.hist_cur == NULL ||
708 (ministate.hist_cur = TAILQ_PREV(ministate.hist_cur, mhisthead, entries)) == NULL) {
709 ministate.hist_cur = TAILQ_LAST(&ministate.history->head, mhisthead);
710 ministate.hist_off = ministate.history->len - 1;
711 if (ministate.hist_cur == NULL)
712 message("No prev history item");
713 } else {
714 ministate.hist_off--;
717 if (ministate.hist_cur != NULL)
718 buffer->current_line->line = ministate.hist_cur->h;
721 void
722 cmd_mini_next_history_element(struct buffer *buffer)
724 if (ministate.history == NULL) {
725 message("No history");
726 return;
729 if (ministate.hist_cur == NULL ||
730 (ministate.hist_cur = TAILQ_NEXT(ministate.hist_cur, entries)) == NULL) {
731 ministate.hist_cur = TAILQ_FIRST(&ministate.history->head);
732 ministate.hist_off = 0;
733 if (ministate.hist_cur == NULL)
734 message("No next history item");
735 } else {
736 ministate.hist_off++;
739 if (ministate.hist_cur != NULL)
740 buffer->current_line->line = ministate.hist_cur->h;
743 void
744 cmd_previous_completion(struct buffer *buffer)
746 if (in_minibuffer != MB_COMPREAD)
747 return;
749 buffer = &ministate.compl.buffer;
751 if (buffer->current_line != NULL)
752 buffer->current_line->parent->type = LINE_COMPL;
754 forward_line(buffer, -1);
756 if (buffer->current_line != NULL)
757 buffer->current_line->parent->type = LINE_COMPL_CURRENT;
760 void
761 cmd_next_completion(struct buffer *buffer)
763 if (in_minibuffer != MB_COMPREAD)
764 return;
766 buffer = &ministate.compl.buffer;
768 if (buffer->current_line != NULL)
769 buffer->current_line->parent->type = LINE_COMPL;
771 forward_line(buffer, +1);
773 if (buffer->current_line != NULL)
774 buffer->current_line->parent->type = LINE_COMPL_CURRENT;
777 void
778 cmd_insert_current_candidate(struct buffer *buffer)
780 struct vline *vl;
782 if (in_minibuffer != MB_COMPREAD)
783 return;
785 buffer = &ministate.compl.buffer;
786 if ((vl = buffer->current_line) == NULL)
787 return;
789 minibuffer_taint_hist();
790 strlcpy(ministate.buf, vl->parent->line, sizeof(ministate.buf));
791 ministate.buffer.cpoff = utf8_cplen(ministate.buf);