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_inc_fill_column(struct buffer *buffer)
558 if (fill_column == INT_MAX)
559 return;
561 fill_column += 2;
562 message("fill-column: %d", fill_column);
564 ui_schedule_redraw();
567 void
568 cmd_dec_fill_column(struct buffer *buffer)
570 if (fill_column == INT_MAX || fill_column < 8)
571 return;
573 fill_column -= 2;
574 message("fill-column: %d", fill_column);
576 ui_schedule_redraw();
579 void
580 cmd_olivetti_mode(struct buffer *buffer)
582 olivetti_mode = !olivetti_mode;
583 if (olivetti_mode)
584 message("olivetti-mode enabled");
585 else
586 message("olivetti-mode disabled");
588 ui_schedule_redraw();
591 void
592 cmd_mini_delete_char(struct buffer *buffer)
594 char *c, *n;
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 if (*c == '\0')
605 return;
606 n = utf8_next_cp(c);
608 memmove(c, n, strlen(n)+1);
610 recompute_completions(0);
613 void
614 cmd_mini_delete_backward_char(struct buffer *buffer)
616 char *c, *p, *start;
618 if (!in_minibuffer) {
619 message("text is read-only");
620 return;
623 minibuffer_taint_hist();
625 c = utf8_nth(buffer->current_line->line, buffer->cpoff);
626 start = buffer->current_line->line;
627 if (c == start)
628 return;
629 p = utf8_prev_cp(c-1, start);
631 memmove(p, c, strlen(c)+1);
632 buffer->cpoff--;
634 recompute_completions(0);
637 void
638 cmd_mini_kill_line(struct buffer *buffer)
640 char *c;
642 if (!in_minibuffer) {
643 message("text is read-only");
644 return;
647 minibuffer_taint_hist();
648 c = utf8_nth(buffer->current_line->line, buffer->cpoff);
649 *c = '\0';
651 recompute_completions(0);
654 void
655 cmd_mini_abort(struct buffer *buffer)
657 if (!in_minibuffer)
658 return;
660 ministate.abortfn();
663 void
664 cmd_mini_complete_and_exit(struct buffer *buffer)
666 if (!in_minibuffer)
667 return;
669 minibuffer_taint_hist();
670 ministate.donefn();
673 void
674 cmd_mini_previous_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_PREV(ministate.hist_cur, mhisthead, entries)) == NULL) {
683 ministate.hist_cur = TAILQ_LAST(&ministate.history->head, mhisthead);
684 ministate.hist_off = ministate.history->len - 1;
685 if (ministate.hist_cur == NULL)
686 message("No prev history item");
687 } else {
688 ministate.hist_off--;
691 if (ministate.hist_cur != NULL)
692 buffer->current_line->line = ministate.hist_cur->h;
695 void
696 cmd_mini_next_history_element(struct buffer *buffer)
698 if (ministate.history == NULL) {
699 message("No history");
700 return;
703 if (ministate.hist_cur == NULL ||
704 (ministate.hist_cur = TAILQ_NEXT(ministate.hist_cur, entries)) == NULL) {
705 ministate.hist_cur = TAILQ_FIRST(&ministate.history->head);
706 ministate.hist_off = 0;
707 if (ministate.hist_cur == NULL)
708 message("No next history item");
709 } else {
710 ministate.hist_off++;
713 if (ministate.hist_cur != NULL)
714 buffer->current_line->line = ministate.hist_cur->h;
717 void
718 cmd_previous_completion(struct buffer *buffer)
720 if (in_minibuffer != MB_COMPREAD)
721 return;
723 buffer = &ministate.compl.buffer;
725 if (buffer->current_line != NULL)
726 buffer->current_line->parent->type = LINE_COMPL;
728 forward_line(buffer, -1);
730 if (buffer->current_line != NULL)
731 buffer->current_line->parent->type = LINE_COMPL_CURRENT;
734 void
735 cmd_next_completion(struct buffer *buffer)
737 if (in_minibuffer != MB_COMPREAD)
738 return;
740 buffer = &ministate.compl.buffer;
742 if (buffer->current_line != NULL)
743 buffer->current_line->parent->type = LINE_COMPL;
745 forward_line(buffer, +1);
747 if (buffer->current_line != NULL)
748 buffer->current_line->parent->type = LINE_COMPL_CURRENT;
751 void
752 cmd_insert_current_candidate(struct buffer *buffer)
754 struct vline *vl;
756 if (in_minibuffer != MB_COMPREAD)
757 return;
759 buffer = &ministate.compl.buffer;
760 if ((vl = buffer->current_line) == NULL)
761 return;
763 minibuffer_taint_hist();
764 strlcpy(ministate.buf, vl->parent->line, sizeof(ministate.buf));
765 ministate.buffer.cpoff = utf8_cplen(ministate.buf);