diff options
Diffstat (limited to 'lisp/calendar/calendar.el')
-rw-r--r-- | lisp/calendar/calendar.el | 147 |
1 files changed, 87 insertions, 60 deletions
diff --git a/lisp/calendar/calendar.el b/lisp/calendar/calendar.el index 85a5fc0c2bb..71fb76ce213 100644 --- a/lisp/calendar/calendar.el +++ b/lisp/calendar/calendar.el @@ -1,4 +1,4 @@ -;;; calendar.el --- calendar functions +;;; calendar.el --- calendar functions -*- lexical-binding:t -*- ;; Copyright (C) 1988-1995, 1997, 2000-2018 Free Software Foundation, ;; Inc. @@ -114,6 +114,37 @@ (load "cal-loaddefs" nil t) +;; Calendar has historically relied heavily on dynamic scoping. +;; Concretely, this manifests in the use of references to let-bound variables +;; in Custom vars as well as code in diary files. +;; `eval` is hence the core of the culprit. It's used on: +;; - calendar-date-display-form +;; - calendar-time-display-form +;; - calendar-chinese-time-zone +;; - in cal-dst's there are various calls to `eval' but they seem not to refer +;; to let-bound variables, surprisingly. +;; - calendar-date-echo-text +;; - calendar-mode-line-format +;; - cal-tex-daily-string +;; - diary-date-forms +;; - diary-remind-message +;; - calendar-holidays +;; - calendar-location-name +;; - whatever is passed to calendar-string-spread +;; - whatever is passed to calendar-insert-at-column +;; - whatever is passed to diary-sexp-entry +;; - whatever is passed to diary-remind + +(defmacro calendar-dlet* (binders &rest body) + "Like `let*' but using dynamic scoping." + (declare (indent 1) (debug let)) + `(progn + (with-no-warnings ;Silence "lacks a prefix" warnings! + ,@(mapcar (lambda (binder) + `(defvar ,(if (consp binder) (car binder) binder))) + binders)) + (let* ,binders ,@body))) + ;; Avoid recursive load of calendar when loading cal-menu. Yuck. (provide 'calendar) (require 'cal-menu) @@ -371,7 +402,7 @@ redisplays the diary for whatever date the cursor is moved to." (defcustom calendar-date-echo-text "mouse-2: general menu\nmouse-3: menu for this date" "String displayed when the cursor is over a date in the calendar. -Can be either a fixed string, or a lisp expression that returns one. +Can be either a fixed string, or a Lisp expression that returns one. When this expression is evaluated, DAY, MONTH, and YEAR are integers appropriate to the relevant date. For example, to display the ISO date: @@ -465,8 +496,8 @@ Then redraw the calendar, if necessary." (defcustom calendar-left-margin 5 "Empty space to the left of the first month in the calendar." :group 'calendar - :initialize 'custom-initialize-default - :set 'calendar-set-layout-variable + :initialize #'custom-initialize-default + :set #'calendar-set-layout-variable :type 'integer :version "23.1") @@ -476,7 +507,7 @@ Then redraw the calendar, if necessary." (defcustom calendar-intermonth-spacing 4 "Space between months in the calendar. Minimum value is 1." :group 'calendar - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (calendar-set-layout-variable sym val 1)) :type 'integer @@ -485,7 +516,7 @@ Then redraw the calendar, if necessary." ;; FIXME calendar-month-column-width? (defcustom calendar-column-width 3 "Width of each day column in the calendar. Minimum value is 3." - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (calendar-set-layout-variable sym val 3)) :type 'integer @@ -505,7 +536,7 @@ WIDTH defaults to `calendar-day-header-width'." "Width of the day column headers in the calendar. Must be at least one less than `calendar-column-width'." :group 'calendar - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (or (calendar-customized-p 'calendar-day-header-array) (setq calendar-day-header-array @@ -518,7 +549,7 @@ Must be at least one less than `calendar-column-width'." (defcustom calendar-day-digit-width 2 "Width of the day digits in the calendar. Minimum value is 2." :group 'calendar - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (sym val) (calendar-set-layout-variable sym val 2)) :type 'integer @@ -542,8 +573,8 @@ See `calendar-intermonth-text'." (defcustom calendar-intermonth-text nil "Text to display in the space to the left of each calendar month. -Can be nil, a fixed string, or a lisp expression that returns a string. -When the expression is evaluated, the variables DAY, MONTH and YEAR +Can be nil, a fixed string, or a Lisp expression that returns a string. +When the expression is evaluated, the variables `day', `month' and `year' are integers appropriate for the first day in each week. Will be truncated to the smaller of `calendar-left-margin' and `calendar-intermonth-spacing'. The last character is forced to be a space. @@ -714,7 +745,7 @@ calendar package is already loaded). Rather, use either (const european :tag "Day/Month/Year") (const iso :tag "Year/Month/Day")) :initialize 'custom-initialize-default - :set (lambda (symbol value) + :set (lambda (_symbol value) (calendar-set-date-style value)) :group 'calendar) @@ -939,7 +970,7 @@ Normally you should not customize this, but `calendar-month-header'." calendar-european-month-header) (t calendar-american-month-header)) "Expression to evaluate to return the calendar month headings. -When this expression is evaluated, the variables MONTH and YEAR are +When this expression is evaluated, the variables `month' and `year' are integers appropriate to the relevant month. The result is padded to the width of `calendar-month-digit-width'. @@ -1104,7 +1135,7 @@ MON defaults to `displayed-month'. YR defaults to `displayed-year'." (defmacro calendar-in-read-only-buffer (buffer &rest body) "Switch to BUFFER and execute the forms in BODY. First creates or erases BUFFER as needed. Leaves BUFFER read-only, -with disabled undo. Leaves point at point-min, displays BUFFER." +with disabled undo. Leaves point at `point-min', displays BUFFER." (declare (indent 1) (debug t)) `(progn (set-buffer (get-buffer-create ,buffer)) @@ -1356,7 +1387,7 @@ Optional integers MON and YR are used instead of today's date." (let* ((inhibit-read-only t) (today (calendar-current-date)) (month (calendar-extract-month today)) - (day (calendar-extract-day today)) + ;; (day (calendar-extract-day today)) (year (calendar-extract-year today)) (today-visible (or (not mon) (<= (abs (calendar-interval mon yr month year)) 1))) @@ -1458,8 +1489,9 @@ line." (goto-char (point-min)) (calendar-move-to-column indent) (insert - (calendar-string-spread (list calendar-month-header) - ?\s calendar-month-digit-width)) + (calendar-dlet* ((month month) (year year)) + (calendar-string-spread (list calendar-month-header) + ?\s calendar-month-digit-width))) (calendar-ensure-newline) (calendar-insert-at-column indent calendar-intermonth-header trunc) ;; Use the first N characters of each day to head the columns. @@ -1474,7 +1506,8 @@ line." calendar-day-header-width nil ?\s) (make-string (- calendar-column-width calendar-day-header-width) ?\s))) (calendar-ensure-newline) - (calendar-insert-at-column indent calendar-intermonth-text trunc) + (calendar-dlet* ((day day) (month month) (year year)) + (calendar-insert-at-column indent calendar-intermonth-text trunc)) ;; Add blank days before the first of the month. (insert (make-string (* blank-days calendar-column-width) ?\s)) ;; Put in the days of the month. @@ -1484,7 +1517,8 @@ line." (insert (propertize (format (format "%%%dd" calendar-day-digit-width) day) 'mouse-face 'highlight - 'help-echo (eval calendar-date-echo-text) + 'help-echo (calendar-dlet* ((day day) (month month) (year year)) + (eval calendar-date-echo-text)) ;; 'date property prevents intermonth text confusing re-searches. ;; (Tried intangible, it did not really work.) 'date t) @@ -1494,7 +1528,8 @@ line." (/= day last)) (calendar-ensure-newline) (setq day (1+ day)) ; first day of next week - (calendar-insert-at-column indent calendar-intermonth-text trunc))))) + (calendar-dlet* ((day day) (month month) (year year)) + (calendar-insert-at-column indent calendar-intermonth-text trunc)))))) (defun calendar-redraw () "Redraw the calendar display, if `calendar-buffer' is live." @@ -1754,25 +1789,22 @@ For a complete description, see the info node `Calendar/Diary'. ;; so let's make sure they're always set. Most likely, this will be reset ;; soon in calendar-generate, but better safe than sorry. (unless (boundp 'displayed-month) (setq displayed-month 1)) - (unless (boundp 'displayed-year) (setq displayed-year 2001)) - (if (bound-and-true-p calendar-font-lock-keywords) - (set (make-local-variable 'font-lock-defaults) - '(calendar-font-lock-keywords t)))) + (unless (boundp 'displayed-year) (setq displayed-year 2001))) (defun calendar-string-spread (strings char length) "Concatenate list of STRINGS separated with copies of CHAR to fill LENGTH. -The effect is like mapconcat but the separating pieces are as balanced as +The effect is like `mapconcat' but the separating pieces are as balanced as possible. Each item of STRINGS is evaluated before concatenation so it can actually be an expression that evaluates to a string. If LENGTH is too short, the STRINGS are just concatenated and the result truncated." -;; The algorithm is based on equation (3.25) on page 85 of Concrete -;; Mathematics by Ronald L. Graham, Donald E. Knuth, and Oren Patashnik, -;; Addison-Wesley, Reading, MA, 1989. - (let* ((strings (mapcar 'eval + ;; The algorithm is based on equation (3.25) on page 85 of Concrete + ;; Mathematics by Ronald L. Graham, Donald E. Knuth, and Oren Patashnik, + ;; Addison-Wesley, Reading, MA, 1989. + (let* ((strings (mapcar #'eval (if (< (length strings) 2) (append (list "") strings (list "")) strings))) - (n (- length (string-width (apply 'concat strings)))) + (n (- length (string-width (apply #'concat strings)))) (m (* (1- (length strings)) (char-width char))) (s (car strings)) (strings (cdr strings)) @@ -1789,17 +1821,18 @@ the STRINGS are just concatenated and the result truncated." (if (and calendar-mode-line-format (bufferp (get-buffer calendar-buffer))) (with-current-buffer calendar-buffer - (let ((start (- calendar-left-margin 2)) - (date (condition-case nil - (calendar-cursor-to-nearest-date) - (error (calendar-current-date))))) - (setq mode-line-format - (concat (make-string (max 0 (+ start - (- (car (window-inside-edges)) - (car (window-edges))))) ?\s) - (calendar-string-spread - (mapcar 'eval calendar-mode-line-format) - ?\s (- calendar-right-margin (1- start)))))) + (let ((start (- calendar-left-margin 2))) + (calendar-dlet* ((date (condition-case nil + (calendar-cursor-to-nearest-date) + (error (calendar-current-date))))) + (setq mode-line-format + (concat (make-string (max 0 (+ start + (- (car (window-inside-edges)) + (car (window-edges))))) + ?\s) + (calendar-string-spread + calendar-mode-line-format + ?\s (- calendar-right-margin (1- start))))))) (force-mode-line-update)))) (defun calendar-buffer-list () @@ -2033,11 +2066,11 @@ is a string to insert in the minibuffer before reading." Each abbreviation is no longer than MAXLEN (default `calendar-abbrev-length') characters." (or maxlen (setq maxlen calendar-abbrev-length)) - (apply 'vector (mapcar - (lambda (f) - ;; TODO? truncate-string-to-width? - (substring f 0 (min maxlen (length f)))) - full))) + (apply #'vector (mapcar + (lambda (f) + ;; TODO? truncate-string-to-width? + (substring f 0 (min maxlen (length f)))) + full))) (defcustom calendar-day-name-array ["Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"] @@ -2255,7 +2288,7 @@ If optional NODAY is t, does not ask for day, but just returns (month (cdr (assoc-string (completing-read "Month name: " - (mapcar 'list (append month-array nil)) + (mapcar #'list (append month-array nil)) nil t) (calendar-make-alist month-array 1) t))) (last (calendar-last-day-of-month month year))) @@ -2277,13 +2310,6 @@ Negative years are interpreted as years BC; -1 being 1 BC, and so on." (+ (* 12 (- yr2 yr1)) (- mon2 mon1))) -(defvar calendar-font-lock-keywords nil - "Default keywords to highlight in Calendar mode.") - -(make-obsolete-variable 'calendar-font-lock-keywords - "set font-lock keywords in `calendar-mode-hook', \ -or customize calendar faces." "24.4") - (defun calendar-day-name (date &optional abbrev absolute) "Return a string with the name of the day of the week of DATE. DATE should be a list in the format (MONTH DAY YEAR), unless the @@ -2323,7 +2349,7 @@ interpreted as BC; -1 being 1 BC, and so on." (setq calendar-mark-holidays-flag nil calendar-mark-diary-entries-flag nil) (with-current-buffer calendar-buffer - (mapc 'delete-overlay (overlays-in (point-min) (point-max))))) + (mapc #'delete-overlay (overlays-in (point-min) (point-max))))) (defun calendar-date-is-visible-p (date) "Return non-nil if DATE is valid and is visible in the calendar window." @@ -2426,7 +2452,7 @@ ATTRLIST is a list with elements of the form :face face :foreground color." (make-face temp-face) (copy-face face temp-face) ;; Apply the font aspects. - (apply 'set-face-attribute temp-face nil (nreverse faceinfo)) + (apply #'set-face-attribute temp-face nil (nreverse faceinfo)) temp-face))) (defun calendar-mark-visible-date (date &optional mark) @@ -2498,13 +2524,14 @@ and day names to be abbreviated as specified by `calendar-month-abbrev-array' and `calendar-day-abbrev-array', respectively. An optional parameter NODAYNAME, when t, omits the name of the day of the week." - (let* ((dayname (unless nodayname (calendar-day-name date abbreviate))) - (month (calendar-extract-month date)) + (let ((month (calendar-extract-month date))) + (calendar-dlet* + ((dayname (unless nodayname (calendar-day-name date abbreviate))) (monthname (calendar-month-name month abbreviate)) (day (number-to-string (calendar-extract-day date))) (month (number-to-string month)) (year (number-to-string (calendar-extract-year date)))) - (mapconcat 'eval calendar-date-display-form ""))) + (mapconcat #'eval calendar-date-display-form "")))) (defun calendar-dayname-on-or-before (dayname date) "Return the absolute date of the DAYNAME on or before absolute DATE. @@ -2607,11 +2634,11 @@ If called by a mouse-event, pops up a menu with the result." selection) (if (mouse-event-p event) (and (setq selection (cal-menu-x-popup-menu event title - (mapcar 'list others))) + (mapcar #'list others))) (call-interactively selection)) (calendar-in-read-only-buffer calendar-other-calendars-buffer (calendar-set-mode-line title) - (insert (mapconcat 'identity others "\n")))))) + (insert (mapconcat #'identity others "\n")))))) (defun calendar-print-day-of-year () "Show day number in year/days remaining in year for date under the cursor." |