Subject: [PATCH] Recenter for the calendar
From: Manuel Giraud <[email-masked]>
To: [email-masked]
Date: Fri, 02 May 2025 09:50:07 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

Tags: patch

Hi,

This patch adds a "recenter month at point" functionality to the
calendar.  It also mimics what 'recenter-top-bottom' is doing for
regular buffers.

In GNU Emacs 31.0.50 (build 1, x86_64-unknown-openbsd7.7, X toolkit) of
 2025-05-02 built on computer
Repository revision: b9319616ed63dfb2291f6515828ae450aedfe6fa
Repository branch: mgi/cal-recenter
Windowing system distributor 'The X.Org Foundation', version 11.0.12101016
System Description: OpenBSD computer 7.7 GENERIC.MP#626 amd64

Configured using:
 'configure CC=egcc CPPFLAGS=-I/usr/local/include
 LDFLAGS=-L/usr/local/lib MAKEINFO=gmakeinfo --prefix=/home/manuel/emacs
 --bindir=/home/manuel/bin --with-x-toolkit=lucid
 --with-toolkit-scroll-bars=no --without-cairo
 --without-compress-install'

From 594823776de1231dc0cc9e7053493f56d110b6e3 Mon Sep 17 00:00:00 2001
From: Manuel Giraud <[email-masked]>
Date: Thu, 1 May 2025 17:06:07 +0200
Subject: [PATCH] Recenter for the calendar

* lisp/calendar/cal-move.el (calendar-recenter-last-op): New
variable to track last recenter operation.
(calendar-recenter-first-last): New command to recenter the
calendar.
* lisp/calendar/calendar.el (calendar-mode-map): Keybinding for
this command.
* doc/emacs/calendar.texi (Scroll Calendar): Document this
command.
* etc/NEWS: Announce this command.
---
 doc/emacs/calendar.texi   | 10 ++++++++++
 etc/NEWS                  |  6 ++++++
 lisp/calendar/cal-move.el | 33 +++++++++++++++++++++++++++++++++
 lisp/calendar/calendar.el |  1 +
 4 files changed, 50 insertions(+)

diff --git a/doc/emacs/calendar.texi b/doc/emacs/calendar.texi
index 55798eae16e..836c3f8fd62 100644
--- a/doc/emacs/calendar.texi
+++ b/doc/emacs/calendar.texi
@@ -258,6 +258,8 @@ Scroll Calendar
 @itemx @key{PageUp}
 @itemx @key{prior}
 Scroll backward by three months (@code{calendar-scroll-right-three-months}).
+@item C-l
+Recenter the date at point.
 @end table
 
 @kindex > @r{(Calendar mode)}
@@ -293,6 +295,14 @@ Scroll Calendar
 (or @key{prior}) are equivalent to @kbd{C-v} and @kbd{M-v}, just as
 they are in other modes.
 
+@kindex C-l @r{(Calendar mode)}
+@findex calendar-recenter-first-last
+  The command @kbd{C-l} (@code{calendar-recenter-first-last}) scrolls
+the calendar so that the month of the date at point is centered.  With a
+second invocation, this command puts this same month as the first month
+in the calendar view and a third invocation puts it in last position.
+This is in analogy with @code{recenter-top-bottom}.
+
 @node Counting Days
 @section Counting Days
 
diff --git a/etc/NEWS b/etc/NEWS
index a5936b1c6cf..dd7d6d9015e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1990,6 +1990,12 @@ DISABLE-URI non-nil.
 When starting these debuggers (e.g., 'M-x pdb') while visiting a file,
 pressing 'M-n' in the command prompt suggests a command line including
 the file name, using the minibuffer's "future history".
+** Calendar
+
++++
+*** New command 'calendar-recenter-first-last'.
+This command recenters the month of the date at point.  By default, it
+is bound to 'C-l' in the calendar buffer.
 
 
 * New Modes and Packages in Emacs 31.1
diff --git a/lisp/calendar/cal-move.el b/lisp/calendar/cal-move.el
index b0a8917209f..59f418b9b9c 100644
--- a/lisp/calendar/cal-move.el
+++ b/lisp/calendar/cal-move.el
@@ -217,6 +217,39 @@ calendar-scroll-right-three-months
                      last-nonmenu-event))
   (calendar-scroll-left (* -3 arg) event))
 
+(defvar calendar-recenter-last-op nil
+  "Last calendar recenter operation performed.")
+
+;;;###cal-autoload
+(defun calendar-recenter-first-last ()
+  "Scroll the calendar so that the month of the date at point is centered.
+The second invocation puts this month in the first place, a third
+invocation puts this month in the last place, and then the order is
+reused in a cyclical manner."
+  (interactive)
+  (let ((positions '(center first last))
+        (cursor-month (calendar-extract-month
+                       (calendar-cursor-to-nearest-date))))
+    ;; Update global last position upon repeat.
+    (setq calendar-recenter-last-op
+          (if (eq this-command last-command)
+              (car (or (cdr (member calendar-recenter-last-op positions))
+                       positions))
+            (car positions)))
+    ;; XXX like most functions in calendar, a span of three displayed
+    ;; months is implied here.
+    (cond ((eq calendar-recenter-last-op 'center)
+           (cond ((= cursor-month (1- displayed-month))
+                  (calendar-scroll-right))
+                 ((= cursor-month (1+ displayed-month))
+                  (calendar-scroll-left))))
+          ;; Other sub-cases should not happen as we should be centered
+          ;; from here.
+          ((eq calendar-recenter-last-op 'first)
+           (calendar-scroll-left))
+          ((eq calendar-recenter-last-op 'last)
+           (calendar-scroll-right 2)))))
+
 ;;;###cal-autoload
 (defun calendar-forward-day (arg)
   "Move the cursor forward ARG days.
diff --git a/lisp/calendar/calendar.el b/lisp/calendar/calendar.el
index 058982647fe..2eebc47db28 100644
--- a/lisp/calendar/calendar.el
+++ b/lisp/calendar/calendar.el
@@ -1593,6 +1593,7 @@ calendar-mode-map
     (define-key map "\C-x>" 'calendar-scroll-left)
     (define-key map [next]  'calendar-scroll-left-three-months)
     (define-key map "\C-v"  'calendar-scroll-left-three-months)
+    (define-key map "\C-l"  'calendar-recenter-first-last)
     (define-key map "\C-b"  'calendar-backward-day)
     (define-key map "\C-p"  'calendar-backward-week)
     (define-key map "\e{"   'calendar-backward-month)
-- 
2.49.0

-- 
Manuel Giraud