From bed552a905bc4df34abc4acfdc8a9f0fcc6f8c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=AF=E3=82=A8=E3=83=B3=E9=85=B8?= Date: Sun, 19 May 2024 22:19:26 +0800 Subject: [PATCH] Add commands `next-buffer` / `previous-buffer` (#20) * Bind `next-buffer` to `C-x C-right` and `previous-buffer` to `C-x left` just like GNU Emacs * next-buffer & previous-buffer skip buffers with BF_SYSTEM flag. * next-buffer & previous-buffer command is ignored from minibuffer and popups. * fix compilation errors on linux --- TODO.md | 2 -- qe.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ qe.h | 2 ++ qeconfig.h | 6 ++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 37e889f..49e911e 100644 --- a/TODO.md +++ b/TODO.md @@ -62,8 +62,6 @@ an EditBuffer could also have an embedded QECursor with no EditState * use hash tables for command and variable names * sort key binding tables? -* `next-buffer` command on C-x C-right -* `previous-buffer` command on C-x C-left * `save-some-buffers` command on C-x s * standardize key prefixes: C-, M-, S- and possibly others * use C-M- prefix instead of M-C- in source and documentation but support diff --git a/qe.c b/qe.c index b0c80e1..3402812 100644 --- a/qe.c +++ b/qe.c @@ -7422,6 +7422,67 @@ void do_switch_to_buffer(EditState *s, const char *bufname) switch_to_buffer(s, b); } +/* Find next non-system buffer from the specified buffer. If the specified + buffer is the last one, returns the first one. If only one non-system buffer + exists currently, return itself. */ +static EditBuffer *next_non_system_buffer(EditState *s, EditBuffer *b) { + EditBuffer *nb = b == NULL ? NULL : b->next; + if (nb == NULL) { + nb = s->qe_state->first_buffer; + } + /* qemacs guarantees at least one non-system buffer (i.e. `*scratch*`) exists */ + while (nb->flags & BF_SYSTEM) { + nb = nb->next; + if (nb == NULL) { + nb = s->qe_state->first_buffer; + } + } + return nb; +} + +void do_next_buffer(EditState *s) +{ + EditBuffer *b; + + /* ignore command from the minibuffer and popups */ + if (s->flags & (WF_POPUP | WF_MINIBUF)) + return; + + b = next_non_system_buffer(s, s->b); + switch_to_buffer(s, b); +} + +void do_previous_buffer(EditState *s) +{ + // TODO(chqrlie): simplify this by counting non system buffers + // also handle universal argument + EditBuffer *orig, *curr, *curr_next; + + /* ignore command from the minibuffer and popups */ + if (s->flags & (WF_POPUP | WF_MINIBUF)) + return; + + /* what we want to compare & find are non-system buffers, so need to ignore + all non-system buffers; or it may fall into an infinite loop. */ + + /* original buffer is immutable */ + orig = s->b; + if (orig->flags & BF_SYSTEM) { + orig = next_non_system_buffer(s, orig); + } + /* find from first buffer*/ + curr = s->qe_state->first_buffer; + if (curr->flags & BF_SYSTEM) { + curr = next_non_system_buffer(s, curr); + } + curr_next = next_non_system_buffer(s, curr); + while (curr_next != orig) { + curr = curr_next; + curr_next = next_non_system_buffer(s, curr_next); + } + switch_to_buffer(s, curr); +} + void do_toggle_read_only(EditState *s) { s->b->flags ^= BF_READONLY; diff --git a/qe.h b/qe.h index c11bbcc..3ff7831 100644 --- a/qe.h +++ b/qe.h @@ -1403,6 +1403,8 @@ void display_mode_line(EditState *s); int edit_set_mode(EditState *s, ModeDef *m); void qe_set_next_mode(EditState *s, int n, int status); void do_set_next_mode(EditState *s, int n); +void do_next_buffer(EditState *s); +void do_previous_buffer(EditState *s); /* loading files */ void do_exit_qemacs(EditState *s, int argval); diff --git a/qeconfig.h b/qeconfig.h index 8fdb52d..b311534 100644 --- a/qeconfig.h +++ b/qeconfig.h @@ -224,6 +224,12 @@ static const CmdDef basic_commands[] = { do_kill_buffer, ESsi, "s{Kill buffer: }[buffer]|buffer|" "v", 0) + CMD0( "next-buffer", "C-x C-right", + "Switch to the next buffer", + do_next_buffer) + CMD0( "previous-buffer", "C-x C-left", + "Switch to the previous buffer", + do_previous_buffer) CMD0( "toggle-read-only", "C-x C-q, C-c %", "Toggle the read-only flag of the current buffer", do_toggle_read_only)