X-Git-Url: http://gitweb.michael.orlitzky.com/?p=nagios-mode.git;a=blobdiff_plain;f=nagios-mode.el;h=097e331e6aef5f628dbb24ddf844c084c3b7dc76;hp=5dd7274fe7343f91e2e9358af759d0247b0f736a;hb=HEAD;hpb=2ecf92ea2cab2a75ac0b620a5efb3ab468c7f528 diff --git a/nagios-mode.el b/nagios-mode.el index 5dd7274..0dd0773 100644 --- a/nagios-mode.el +++ b/nagios-mode.el @@ -1,23 +1,18 @@ -;; -;; nagios-mode, an Emacs mode for Nagios -;; configuration files. -;; -;; Copyright Michael Orlitzky -;; -;; http://michael.orlitzky.com/ +;; nagios-mode: an emacs mode for nagios configuration files +;; Copyright (C) 2024 Michael Orlitzky ;; ;; This program is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. +;; it under the terms of the GNU Affero General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. -;; -;; http://www.fsf.org/licensing/licenses/gpl.html +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU Affero General Public License for more details. ;; +;; You should have received a copy of the GNU Affero General Public License +;; along with this program. If not, see . (require 'font-lock) (require 'regexp-opt) @@ -36,64 +31,138 @@ (defun nagios-indent-line(&optional flag) "Indents a line, taking nesting into account." + (interactive) (nagios-indent-to (nagios-calculate-indent)) ) +(defun nagios-beginning-of-line-pos() + ;; Return the point position corresponding to the beginning + ;; of the current line. + (save-excursion + (beginning-of-line) + (point) + ) +) -(defun nagios-indent-to(indent-column) - "Indent the current line to column indent-column." - (setq pos (point)) - (beginning-of-line) - (setq bol (point)) - (setq pos-offset (- pos bol)) +(defun nagios-end-of-line-pos() + ;; Return the point position corresponding to the end + ;; of the current line. + (save-excursion + (end-of-line) + (point) + ) +) - (setq first-char-offset - (skip-chars-forward " \t")) +(defun nagios-point-offset() + ;; How far are we from the beginning of the line? + (- (point) (nagios-beginning-of-line-pos)) +) - (setq first-char-pos - (+ bol first-char-offset)) - - (delete-region bol first-char-pos) - - (beginning-of-line) +(defun nagios-first-char-offset() + ;; How far is the first character on this line + ;; from the beginning of the line? + (save-excursion + (beginning-of-line) + (skip-chars-forward " \t") + ) +) - (setq pos-change (- indent-column first-char-offset)) - (setq pos-offset (+ pos-offset pos-change)) +(defun nagios-first-char-pos() + ;; What's the position of the first character on this line? + (+ (nagios-beginning-of-line-pos) (nagios-first-char-offset)) +) - (if (<= pos-offset indent-column) - (setq pos-offset indent-column)) - - (while (< 0 indent-column) - (insert " ") - (setq indent-column (- indent-column 1))) +(defun nagios-indent-to(indent-column) + "Indent the current line to column indent-column." + ;; Store the point in orig-pos. + (let ((orig-point (point))) + + ;; And store the offset of the first character (with respect to the + ;; beginning of the line) in orig-first-char-offset. + (let ((orig-first-char-offset (nagios-first-char-offset))) + + ;; Delete any leading whitespace, and move the point to the + ;; beginning of the line. + (delete-region (nagios-beginning-of-line-pos) (nagios-first-char-pos)) + (beginning-of-line) + + ;; Now insert indent-column spaces. + (let ((indent-remaining indent-column)) + (while (< 0 indent-remaining) + (insert " ") + (setq indent-remaining (- indent-remaining 1))) + ) + + ;; The text on the current line just moved left/right some amount; + ;; call it text-delta. We want to move the point that same distance. + (let ((text-delta (- (nagios-first-char-offset) orig-first-char-offset))) + (goto-char (+ orig-point text-delta)) + ) + + ;; The point should never wind up to the left of indent-column, so + ;; if it's there, move it over to indent-column. + (if (< (nagios-point-offset) indent-column) + (goto-char (+ (nagios-beginning-of-line-pos) indent-column)) + ) + ) + ) +) - (goto-char (+ bol pos-offset)) +(defun nagios-char-is-commented(pos) + "True if the character at position pos is commented, nil otherwise." + (save-excursion + (goto-char pos) + (re-search-backward "\\(#\\|;\\)" (nagios-beginning-of-line-pos) t) ) +) +(defun nagios-char-is-commented-and-valid(pos) + "True if the character at position pos is commented and non-nil. + Nil otherwise." + (if (eq nil pos) + nil + (nagios-char-is-commented pos) + ) +) -(defun last-opening-brace() - ;; Get the position of the last opening brace, with - ;; respect to the current point. +(defun nagios-last-opening-brace() + "Returns the position of the last opening brace, with + respect to the current point. Ignores braces which + are commented out." (save-excursion (let ((lob (re-search-backward "{" nil t))) + + (while (nagios-char-is-commented-and-valid lob) + (goto-char lob) + (setq lob (re-search-backward "{" nil t)) + ) + (if lob - lob - -1) + lob + -1) ) ) ) -(defun last-closing-brace() - ;; Get the position of the last closing brace, with - ;; respect to the current point. + +(defun nagios-last-closing-brace() + "Get the position of the last closing brace, with + respect to the current point. Ignores braces which + are commented out." (save-excursion (let ((lcb (re-search-backward "}" nil t))) + + (while (nagios-char-is-commented-and-valid lcb) + (goto-char lcb) + (setq lcb (re-search-backward "}" nil t)) + ) + (if lcb - lcb - -1) + lcb + -1) ) ) ) @@ -103,38 +172,42 @@ ;; If the last brace seen in the buffer is an opening brace, we're ;; in a block. Otherwise, we aren't. - (if (>= (last-closing-brace) (last-opening-brace)) + (if (>= (nagios-last-closing-brace) (nagios-last-opening-brace)) nil t) ) +(defun nagios-brace-on-line() + ;; Is there a curly brace on this line? + (save-excursion + (beginning-of-line) + (re-search-forward "[{}]" (nagios-end-of-line-pos) t) + ) +) + (defun nagios-calculate-indent() "Calculate the level of indentation." - ;; We're either inside a block, or we aren't. - (setq indent 0) - - (if (nagios-in-block) - (setq indent nagios-indent-level)) - - (setq pos (point)) - (end-of-line) - (setq eol (point)) - (beginning-of-line) - (setq bol (point)) - - ;; Set the indentation level to 0 if we find either brace on this - ;; line. - (if (re-search-forward "[{}]" eol t) - (setq indent 0)) - - (goto-char pos) - - indent + ;; We're either inside a block, or we aren't. + ;; Initialize the indent variable to either nagios-indent-level + ;; or 0 depending on whether or not we're in a block. + (let ((indent (if (nagios-in-block) + nagios-indent-level + 0) + ) + ) + + ;; Set the indentation level to 0 if we find either brace on this + ;; line and. + (if (and (nagios-brace-on-line) + (not (nagios-char-is-commented (nagios-brace-on-line)))) + 0 + indent + ) ) - +) ;; Keymaps @@ -156,26 +229,6 @@ (read-kbd-macro "}") 'nagios-insert-right-brace-and-indent) ) - - - -;; Regular Expression Transformations - -(defun regexp-alt-raw(element-list) - "Takes a list of elements, and returns the string '\\(element1\\|element2...\\)'" - - ;; This is necessary since regexp-opt does not accept regular - ;; expressions as arguments. We use regexp-opt when we can, of - ;; course. - - (let ((regexp "\\(")) - (mapcar (lambda(elem) - (setq regexp (concat regexp "\\(" elem "\\)" "\\|"))) - element-list) - (concat (substring regexp 0 -2) ; Cut the last "\\|" - "\\)") - ) - ) @@ -184,31 +237,124 @@ (concat "^[ \t\r\n]*" (regexp-opt - '("active_checks_enabled" "address" "alias" "check_command" - "check_freshness" "check_interval" "check_period" "checks_enabled" - "command_line" "command_name" "contactgroups" "contact_groups" - "contactgroup_members" "contact_name" "contactgroup_name" "contacts" - "dependent_host_name" "dependent_service_description" "email" - "event_handler" "event_handler_enabled" "execution_failure_criteria" - "failure_prediction_enabled" "first_notification" - "first_notification_delay" "flap_detection_enabled" "freshness_threshold" - "friday" "high_flap_threshold" "host_name" "host_notification_commands" - "host_notification_options" "host_notification_period" - "host_notifications_enabled" "hostgroup_name" "hostgroups" - "is_volatile" "last_notification" "low_flap_threshold" - "max_check_attempts" "members" "monday" "normal_check_interval" - "notes" "notification_failure_criteria" - "notification_interval" "notification_options" - "notification_period" "notifications_enabled" - "obsess_over_service" "pager" "parallelize_check" - "parents" "passive_checks_enabled" - "process_perf_data" "retain_nonstatus_information" - "retain_status_information" "retry_check_interval" - "retry_interval" "saturday" "service_description" - "service_notification_commands" "service_notification_options" - "service_notification_period" "service_notifications_enabled" - "servicegroup_name" "stalking_options" - "sunday" "thursday" "timeperiod_name" "tuesday" "wednesday") t) + '("action_url" + "active_checks_enabled" + "address" + "alias" + "can_submit_commands" + "check_command" + "check_freshness" + "check_interval" + "check_period" + "checks_enabled" + "command_line" + "command_name" + "contact_groups" + "contact_name" + "contactgroup_members" + "contactgroup_name" + "contactgroups" + "contacts" + "dependency_period" + "dependent_description" + "dependent_host" + "dependent_host_name" + "dependent_hostgroup" + "dependent_hostgroup_name" + "dependent_hostgroups" + "dependent_service_description" + "dependent_servicegroup" + "dependent_servicegroup_name" + "dependent_servicegroups" + "description" + "display_name" + "email" + "escalation_options" + "escalation_period" + "event_handler" + "event_handler_enabled" + "exclude" + "execution_failure_criteria" + "execution_failure_options" + "failure_prediction_enabled" + "failure_prediction_options" + "first_notification" + "first_notification_delay" + "flap_detection_enabled" + "flap_detection_options" + "freshness_threshold" + "friday" + "high_flap_threshold" + "host" + "host_groups" + "host_name" + "host_notification_commands" + "host_notification_options" + "host_notification_period" + "host_notifications_enabled" + "hostgroup" + "hostgroup_members" + "hostgroup_name" + "hostgroups" + "hosts" + "hourly_value" + "icon_image" + "icon_image_alt" + "importance" + "inherits_parent" + "initial_state" + "is_volatile" + "last_notification" + "low_flap_threshold" + "master_description" + "master_host" + "master_host_name" + "master_service_description" + "max_check_attempts" + "members" + "minimum_importance" + "minimum_value" + "monday" + "normal_check_interval" + "notes" + "notes_url" + "notification_failure_criteria" + "notification_failure_options" + "notification_interval" + "notification_options" + "notification_period" + "notifications_enabled" + "obsess" + "obsess_over_host" + "obsess_over_service" + "pager" + "parallelize_check" + "parents" + "passive_checks_enabled" + "process_perf_data" + "retain_nonstatus_information" + "retain_status_information" + "retry_check_interval" + "retry_interval" + "saturday" + "service_description" + "service_groups" + "service_notification_commands" + "service_notification_options" + "service_notification_period" + "service_notifications_enabled" + "servicegroup" + "servicegroup_members" + "servicegroup_name" + "servicegroups" + "stalking_options" + "statusmap_image" + "sunday" + "thursday" + "timeperiod_name" + "tuesday" + "vrml_image" + "wednesday") t) "[ \r\n\t]+") ) @@ -271,6 +417,7 @@ "$HOSTACTIONURL$" "$HOSTADDRESS$" "$HOSTALIAS$" + "$HOSTANDSERVICESIMPORTANCE$" "$HOSTATTEMPT$" "$HOSTCHECKCOMMAND$" "$HOSTCHECKTYPE$" @@ -282,17 +429,22 @@ "$HOSTEXECUTIONTIME$" "$HOSTGROUPACTIONURL$" "$HOSTGROUPALIAS$" + "$HOSTGROUPMEMBERADDRESSES$" "$HOSTGROUPMEMBERS$" "$HOSTGROUPNAME$" "$HOSTGROUPNAMES$" "$HOSTGROUPNOTES$" "$HOSTGROUPNOTESURL$" + "$HOSTIMPORTANCE$" + "$HOSTINFOURL$" "$HOSTLATENCY$" "$HOSTNAME$" "$HOSTNOTES$" "$HOSTNOTESURL$" + "$HOSTNOTIFICATIONENABLED$" "$HOSTNOTIFICATIONID$" "$HOSTNOTIFICATIONNUMBER$" + "$HOSTNOTIFICATIONPERIOD$" "$HOSTOUTPUT$" "$HOSTPERCENTCHANGE$" "$HOSTPERFDATA$" @@ -363,12 +515,16 @@ "$SERVICEGROUPNAMES$" "$SERVICEGROUPNOTES$" "$SERVICEGROUPNOTESURL$" + "$SERVICEIMPORTANCE$" + "$SERVICEINFOURL$" "$SERVICEISVOLATILE$" "$SERVICELATENCY$" "$SERVICENOTES$" "$SERVICENOTESURL$" + "$SERVICENOTIFICATIONENABLED$" "$SERVICENOTIFICATIONID$" "$SERVICENOTIFICATIONNUMBER$" + "$SERVICENOTIFICATIONPERIOD$" "$SERVICEOUTPUT$" "$SERVICEPERCENTCHANGE$" "$SERVICEPERFDATA$" @@ -669,27 +825,31 @@ (concat "^[ \t\r\n]*" - (regexp-alt-raw - '("define command" - "define contact" - "define contactgroup" - "define host" - "define hostdependency" - "define hostescalation" - "define hostextinfo" - "define hostgroup" - "define hostgroupescalation" - "define null" - "define service" - "define servicedependency" - "define serviceescalation" - "define serviceextinfo" - "define servicegroup" - "define timeperiod")) - - ;; These can be "terminated" by either an opening curly - ;; brace, or a space. - "\\({\\| \\)") + "\\(" ;; Stick parenthesis around whatever comes out + ;; of regexp-opt. We use this to match a + ;; subexpression during font-lock. + (regexp-opt + '("define command" + "define contact" + "define contactgroup" + "define host" + "define hostdependency" + "define hostescalation" + "define hostextinfo" + "define hostgroup" + "define null" + "define service" + "define servicedependency" + "define serviceescalation" + "define serviceextinfo" + "define servicegroup" + "define timeperiod")) + ;; This closes the parentheses that we opened + "\\)" ;; before regexp-opt. + + ;; These can be "terminated" by either an opening curly + ;; brace, or a space. + "\\({\\| \\)") ) ) @@ -744,23 +904,17 @@ (make-local-variable 'comment-start-skip) (make-local-variable 'comment-end) (make-local-variable 'indent-line-function) - (make-local-variable 'syntax-begin-function) (set-syntax-table nagios-mode-syntax-table) - + (setq mode-name "nagios" - major-mode 'nagios-mode - indent-line-function 'nagios-indent-line - font-lock-defaults '(nagios-font-lock-keywords) - comment-start "#" - comment-start-skip "#\|; +" - comment-end "" - - ;; Since comments and strings do not span multiple lines, - ;; the syntax parser can safely start parsing at the beginning - ;; of any line. - syntax-begin-function 'beginning-of-line - ) + major-mode 'nagios-mode + indent-line-function 'nagios-indent-line + font-lock-defaults '(nagios-font-lock-keywords) + comment-start "#" + comment-start-skip "#\|; +" + comment-end "" + ) ;; Keyboard Mapping (use-local-map nagios-mode-map)