└── crontab-mode.el /crontab-mode.el: -------------------------------------------------------------------------------- 1 | ;;; crontab-mode.el --- Major mode for crontab(5) -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2016 Mario Rodas 4 | 5 | ;; Author: Mario Rodas 6 | ;; URL: https://github.com/emacs-pe/crontab-mode 7 | ;; Keywords: languages 8 | ;; Version: 0.1 9 | ;; Package-Requires: ((emacs "24.3")) 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; License: 14 | 15 | ;; This program is free software: you can redistribute it and/or modify 16 | ;; it under the terms of the GNU General Public License as published by 17 | ;; the Free Software Foundation, either version 3 of the License, or 18 | ;; (at your option) any later version. 19 | 20 | ;; This program is distributed in the hope that it will be useful, 21 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | ;; GNU General Public License for more details. 24 | 25 | ;; You should have received a copy of the GNU General Public License 26 | ;; along with this program. If not, see . 27 | 28 | ;;; Commentary: 29 | 30 | ;; Major mode for crontab(5) files 31 | 32 | ;;; Code: 33 | (eval-when-compile (require 'cl-lib)) 34 | (require 'sh-script) 35 | 36 | (defgroup crontab nil 37 | "Major mode for editing crontab(5) files." 38 | :prefix "crontab-" 39 | :group 'languages) 40 | 41 | (defface crontab-minute 42 | '((t :inherit outline-1)) 43 | "Face to use for highlighting crontab minute field." 44 | :group 'crontab) 45 | 46 | (defface crontab-hour 47 | '((t :inherit outline-2)) 48 | "Face to use for highlighting crontab hour field." 49 | :group 'crontab) 50 | 51 | (defface crontab-month-day 52 | '((t :inherit outline-3)) 53 | "Face to use for highlighting crontab day of month field." 54 | :group 'crontab) 55 | 56 | (defface crontab-month 57 | '((t :inherit outline-4)) 58 | "Face to use for highlighting crontab month field." 59 | :group 'crontab) 60 | 61 | (defface crontab-week-day 62 | '((t :inherit outline-5)) 63 | "Face to use for highlighting crontab day of week field." 64 | :group 'crontab) 65 | 66 | (defface crontab-predefined 67 | '((t :inherit outline-1)) 68 | "Face to use for crontab predefined definitions." 69 | :group 'crontab) 70 | 71 | (defvar crontab-fields '("minute (0-59)" "hour (0-23)" "day (1-31)" "month (1-12)" "day-of-week (0-6)" "command") 72 | "Fields used by `crontab-eldoc-function' to show the crontab information.") 73 | 74 | (eval-and-compile 75 | (defconst crontab-rx-constituents 76 | ;; https://en.wikipedia.org/wiki/Cron#CRON_expression 77 | `((unit . ,(rx (+ (in "-,*" num)))) 78 | (step . ,(rx (? "/" (+ num)))) 79 | (month . ,(rx (or "jan" "feb" "mar" "apr" "may" "jun" "jul" "aug" "sep" "oct" "nov" "dec"))) 80 | (weekday . ,(rx (or "sun" "mon" "tue" "wed" "thu" "fri" "sat")))) 81 | "Additional specific sexps for `crontab-rx'") 82 | 83 | (defmacro crontab-rx (&rest regexps) 84 | "Crontab specialized rx macro." 85 | (let ((rx-constituents (append crontab-rx-constituents rx-constituents))) 86 | (cond ((null regexps) 87 | (error "No regexp")) 88 | ((cdr regexps) 89 | (rx-to-string `(and ,@regexps) t)) 90 | (t 91 | (rx-to-string (car regexps) t)))))) 92 | 93 | (defvar crontab-font-lock-keywords 94 | `( 95 | ;; ┌───────────────────────── min (0 - 59) 96 | ;; │ ┌─────────────────────── hour (0 - 23) 97 | ;; │ │ ┌───────────────────── day of month (1 - 31) 98 | ;; │ │ │ ┌─────────────────── month (1 - 12) 99 | ;; │ │ │ │ ┌───────────────── day of week (0 - 6) (Sunday to Saturday; 7 is also Sunday) 100 | ;; │ │ │ │ │ 101 | ;; │ │ │ │ │ 102 | ;; │ │ │ │ │ 103 | ;; * * * * * command to execute 104 | (,(crontab-rx line-start (* space) 105 | (group unit (? step)) (+ space) ; minutes 106 | (group unit (? step)) (+ space) ; hours 107 | (group (or (seq unit (? step)) "?" "L" "W")) (+ space) ; day of month 108 | (group (or unit month) (? step)) (+ space) ; month 109 | (group (or unit weekday) (? step)) (+ space) ; day of week 110 | (group (+ not-newline)) ; command 111 | line-end) 112 | (1 'crontab-minute) 113 | (2 'crontab-hour) 114 | (3 'crontab-month-day) 115 | (4 'crontab-month) 116 | (5 'crontab-week-day)) 117 | 118 | ;; Nonstandard predefined scheduling definitions 119 | (,(rx line-start (* space) 120 | (group (or "@reboot" "@yearly" "@annually" 121 | "@monthly" "@weekly" "@daily" "@hourly")) 122 | (+ space) 123 | (group (+ not-newline)) ; Command 124 | line-end) 125 | (1 'crontab-predefined)) 126 | 127 | ;; Variables 128 | ("^\\([^#=]+\\)=\\(.*\\)$" 129 | (1 font-lock-variable-name-face) 130 | (2 font-lock-string-face))) 131 | "Info for function `font-lock-mode'.") 132 | 133 | (defun crontab-indent-line () 134 | "Indent current line as crontab mode." 135 | (interactive) 136 | (indent-line-to 0)) 137 | 138 | (defun crontab-eldoc-function (&rest _ignored) 139 | "`eldoc-documentation-function' for Crontab." 140 | (let* ((point (point)) 141 | (end-of-line (point-at-eol)) 142 | (fields (copy-sequence crontab-fields)) 143 | (n (save-excursion 144 | (beginning-of-line) 145 | (cl-loop while (re-search-forward "[^[:space:]]+" end-of-line t) 146 | with field = -1 147 | do (cl-incf field) 148 | if (or (>= (point) point) (>= field 5)) 149 | return field)))) 150 | (when n 151 | (setcar (nthcdr n fields) (propertize (elt fields n) 'face 'eldoc-highlight-function-argument))) 152 | (mapconcat 'identity fields " "))) 153 | 154 | ;;;###autoload 155 | (define-derived-mode crontab-mode text-mode "Crontab" 156 | "Major mode for editing crontab file. 157 | 158 | \\{crontab-mode-map}" 159 | :syntax-table sh-mode-syntax-table 160 | (setq-local comment-start "# ") 161 | (setq-local comment-start-skip "#+\\s-*") 162 | 163 | (if (null eldoc-documentation-function) ; Emacs<25 164 | (setq-local eldoc-documentation-function #'crontab-eldoc-function) 165 | (if (boundp 'eldoc-documentation-functions) 166 | (add-hook 'eldoc-documentation-functions #'crontab-eldoc-function nil t) 167 | (add-function :before-until (local 'eldoc-documentation-function) 168 | #'crontab-eldoc-function))) 169 | 170 | (setq-local font-lock-defaults '(crontab-font-lock-keywords nil t)) 171 | (setq-local indent-line-function #'crontab-indent-line)) 172 | 173 | ;;;###autoload 174 | (add-to-list 'auto-mode-alist '("/crontab\\(\\.X*[[:alnum:]]+\\)?\\'" . crontab-mode)) 175 | 176 | (provide 'crontab-mode) 177 | ;;; crontab-mode.el ends here 178 | --------------------------------------------------------------------------------