diff options
Diffstat (limited to 'lisp/progmodes/flymake.el')
-rw-r--r-- | lisp/progmodes/flymake.el | 276 |
1 files changed, 173 insertions, 103 deletions
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 40eacdd1888..fdb22ccaf34 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -3,8 +3,8 @@ ;; Copyright (C) 2003-2018 Free Software Foundation, Inc. ;; Author: Pavel Kobyakov <pk_at_work@yahoo.com> -;; Maintainer: Leo Liu <sdl.web@gmail.com> -;; Version: 0.3 +;; Maintainer: João Távora <joaotavora@gmail.com> +;; Version: 1.0 ;; Keywords: c languages tools ;; This file is part of GNU Emacs. @@ -34,13 +34,77 @@ ;; results produced by these backends, as well as entry points for ;; backends to hook on to. ;; -;; The main entry points are `flymake-mode' and `flymake-start' +;; The main interactive entry point is the `flymake-mode' minor mode, +;; which periodically and automatically initiates checks as the user +;; is editing the buffer. The variables `flymake-no-changes-timeout', +;; `flymake-start-syntax-check-on-newline' and +;; `flymake-start-on-flymake-mode' give finer control over the events +;; triggering a check, as does the interactive command +;; `flymake-start', which immediately starts a check. ;; -;; The docstrings of these variables are relevant to understanding how -;; Flymake works for both the user and the backend programmer: +;; Shortly after each check, a summary of collected diagnostics should +;; appear in the mode-line. If it doesn't, there might not be a +;; suitable Flymake backend for the current buffer's major mode, in +;; which case Flymake will indicate this in the mode-line. The +;; indicator will be `!' (exclamation mark), if all the configured +;; backends errored (or decided to disable themselves) and `?' +;; (question mark) if no backends were even configured. ;; -;; * `flymake-diagnostic-functions' -;; * `flymake-diagnostic-types-alist' +;; For programmers interested in writing a new Flymake backend, the +;; docstring of `flymake-diagnostic-functions', the Flymake manual, +;; and the code of existing backends are probably a good starting +;; point. +;; +;; The user wishing to customize the appearance of error types should +;; set properties on the symbols associated with each diagnostic type. +;; The standard diagnostic symbols are `:error', `:warning' and +;; `:note' (though a specific backend may define and use more). The +;; following properties can be set: +;; +;; * `flymake-bitmap', an image displayed in the fringe according to +;; `flymake-fringe-indicator-position'. The value actually follows +;; the syntax of `flymake-error-bitmap' (which see). It is overridden +;; by any `before-string' overlay property. +;; +;; * `flymake-severity', a non-negative integer specifying the +;; diagnostic's severity. The higher, the more serious. If the +;; overlay property `priority' is not specified, `severity' is used to +;; set it and help sort overlapping overlays. +;; +;; * `flymake-overlay-control', an alist ((OVPROP . VALUE) ...) of +;; further properties used to affect the appearance of Flymake +;; annotations. With the exception of `category' and `evaporate', +;; these properties are applied directly to the created overlay. See +;; Info Node `(elisp)Overlay Properties'. +;; +;; * `flymake-category', a symbol whose property list is considered a +;; default for missing values of any other properties. This is useful +;; to backend authors when creating new diagnostic types that differ +;; from an existing type by only a few properties. The category +;; symbols `flymake-error', `flymake-warning' and `flymake-note' make +;; good candidates for values of this property. +;; +;; For instance, to omit the fringe bitmap displayed for the standard +;; `:note' type, set its `flymake-bitmap' property to nil: +;; +;; (put :note 'flymake-bitmap nil) +;; +;; To change the face for `:note' type, add a `face' entry to its +;; `flymake-overlay-control' property. +;; +;; (push '(face . highlight) (get :note 'flymake-overlay-control)) +;; +;; If you push another alist entry in front, it overrides the previous +;; one. So this effectively removes the face from `:note' +;; diagnostics. +;; +;; (push '(face . nil) (get :note 'flymake-overlay-control)) +;; +;; To erase customizations and go back to the original look for +;; `:note' types: +;; +;; (cl-remf (symbol-plist :note) 'flymake-overlay-control) +;; (cl-remf (symbol-plist :note) 'flymake-bitmap) ;; ;;; Code: @@ -132,11 +196,17 @@ If nil, never start checking buffer automatically like this." 'flymake-start-on-flymake-mode "26.1") (defcustom flymake-start-on-flymake-mode t - "Start syntax check when `flymake-mode' is enabled. + "If non-nil, start syntax check when `flymake-mode' is enabled. Specifically, start it when the buffer is actually displayed." :version "26.1" :type 'boolean) +(defcustom flymake-start-on-save-buffer t + "If non-nil start syntax check when a buffer is saved. +Specifically, start it when the saved buffer is actually displayed." + :version "27.1" + :type 'boolean) + (defcustom flymake-log-level -1 "Obsolete and ignored variable." :type 'integer) @@ -222,18 +292,21 @@ generated it." (cl-defstruct (flymake--diag (:constructor flymake--diag-make)) - buffer beg end type text backend) + buffer beg end type text backend data) ;;;###autoload (defun flymake-make-diagnostic (buffer beg end type - text) + text + &optional data) "Make a Flymake diagnostic for BUFFER's region from BEG to END. -TYPE is a key to `flymake-diagnostic-types-alist' and TEXT is a -description of the problem detected in this region." - (flymake--diag-make :buffer buffer :beg beg :end end :type type :text text)) +TYPE is a key to symbol and TEXT is a description of the problem +detected in this region. DATA is any object that the caller +wishes to attach to the created diagnostic for later retrieval." + (flymake--diag-make :buffer buffer :beg beg :end end + :type type :text text :data data)) ;;;###autoload (defun flymake-diagnostics (&optional beg end) @@ -257,6 +330,7 @@ diagnostics at BEG." (flymake--diag-accessor flymake-diagnostic-beg flymake--diag-beg beg) (flymake--diag-accessor flymake-diagnostic-end flymake--diag-end end) (flymake--diag-accessor flymake-diagnostic-backend flymake--diag-backend backend) +(flymake--diag-accessor flymake-diagnostic-data flymake--diag-data backend) (cl-defun flymake--overlays (&key beg end filter compare key) "Get flymake-related overlays. @@ -419,74 +493,63 @@ Currently accepted REPORT-KEY arguments are: * `:force': value should be a boolean suggesting that Flymake consider the report even if it was somehow unexpected.") -(defvar flymake-diagnostic-types-alist - `((:error - . ((flymake-category . flymake-error))) - (:warning - . ((flymake-category . flymake-warning))) - (:note - . ((flymake-category . flymake-note)))) - "Alist ((KEY . PROPS)*) of properties of Flymake diagnostic types. -KEY designates a kind of diagnostic can be anything passed as -`:type' to `flymake-make-diagnostic'. - -PROPS is an alist of properties that are applied, in order, to -the diagnostics of the type designated by KEY. The recognized -properties are: - -* Every property pertaining to overlays, except `category' and - `evaporate' (see Info Node `(elisp)Overlay Properties'), used - to affect the appearance of Flymake annotations. - -* `bitmap', an image displayed in the fringe according to - `flymake-fringe-indicator-position'. The value actually - follows the syntax of `flymake-error-bitmap' (which see). It - is overridden by any `before-string' overlay property. - -* `severity', a non-negative integer specifying the diagnostic's - severity. The higher, the more serious. If the overlay - property `priority' is not specified, `severity' is used to set - it and help sort overlapping overlays. - -* `flymake-category', a symbol whose property list is considered - a default for missing values of any other properties. This is - useful to backend authors when creating new diagnostic types - that differ from an existing type by only a few properties.") +(put 'flymake-diagnostic-functions 'safe-local-variable #'null) + +(put :error 'flymake-category 'flymake-error) +(put :warning 'flymake-category 'flymake-warning) +(put :note 'flymake-category 'flymake-note) + +(defvar flymake-diagnostic-types-alist `() "") +(make-obsolete-variable + 'flymake-diagnostic-types-alist + "Set properties on the diagnostic symbols instead. See Info +Node `(Flymake)Flymake error types'" + "27.1") (put 'flymake-error 'face 'flymake-error) -(put 'flymake-error 'bitmap 'flymake-error-bitmap) +(put 'flymake-error 'flymake-bitmap 'flymake-error-bitmap) (put 'flymake-error 'severity (warning-numeric-level :error)) (put 'flymake-error 'mode-line-face 'compilation-error) (put 'flymake-warning 'face 'flymake-warning) -(put 'flymake-warning 'bitmap 'flymake-warning-bitmap) +(put 'flymake-warning 'flymake-bitmap 'flymake-warning-bitmap) (put 'flymake-warning 'severity (warning-numeric-level :warning)) (put 'flymake-warning 'mode-line-face 'compilation-warning) (put 'flymake-note 'face 'flymake-note) -(put 'flymake-note 'bitmap 'flymake-note-bitmap) +(put 'flymake-note 'flymake-bitmap 'flymake-note-bitmap) (put 'flymake-note 'severity (warning-numeric-level :debug)) (put 'flymake-note 'mode-line-face 'compilation-info) (defun flymake--lookup-type-property (type prop &optional default) - "Look up PROP for TYPE in `flymake-diagnostic-types-alist'. -If TYPE doesn't declare PROP in either -`flymake-diagnostic-types-alist' or in the symbol of its + "Look up PROP for diagnostic TYPE. +If TYPE doesn't declare PROP in its plist or in the symbol of its associated `flymake-category' return DEFAULT." - (let ((alist-probe (assoc type flymake-diagnostic-types-alist))) - (cond (alist-probe - (let* ((alist (cdr alist-probe)) - (prop-probe (assoc prop alist))) - (if prop-probe - (cdr prop-probe) - (if-let* ((cat (assoc-default 'flymake-category alist)) - (plist (and (symbolp cat) - (symbol-plist cat))) - (cat-probe (plist-member plist prop))) - (cadr cat-probe) - default)))) - (t - default)))) + ;; This function also consults `flymake-diagnostic-types-alist' for + ;; backward compatibility. + ;; + (if (plist-member (symbol-plist type) prop) + ;; allow nil values to survive + (get type prop) + (let (alist) + (or + (alist-get + prop (setq + alist + (alist-get type flymake-diagnostic-types-alist))) + (when-let* ((cat (or + (get type 'flymake-category) + (alist-get 'flymake-category alist))) + (plist (and (symbolp cat) + (symbol-plist cat))) + (cat-probe (plist-member plist prop))) + (cadr cat-probe)) + default)))) + +(defun flymake--severity (type) + "Get the severity for diagnostic TYPE." + (flymake--lookup-type-property type 'severity + (warning-numeric-level :error))) (defun flymake--fringe-overlay-spec (bitmap &optional recursed) (if (and (symbolp bitmap) @@ -503,34 +566,38 @@ associated `flymake-category' return DEFAULT." (list bitmap))))))) (defun flymake--highlight-line (diagnostic) - "Highlight buffer with info in DIAGNOSTIC." - (when-let* ((ov (make-overlay + "Highlight buffer with info in DIGNOSTIC." + (when-let* ((type (flymake--diag-type diagnostic)) + (ov (make-overlay (flymake--diag-beg diagnostic) (flymake--diag-end diagnostic)))) - ;; First set `category' in the overlay, then copy over every other - ;; property. + ;; First set `category' in the overlay ;; - (let ((alist (assoc-default (flymake--diag-type diagnostic) - flymake-diagnostic-types-alist))) - (overlay-put ov 'category (assoc-default 'flymake-category alist)) - (cl-loop for (k . v) in alist - unless (eq k 'category) - do (overlay-put ov k v))) + (overlay-put ov 'category + (flymake--lookup-type-property type 'flymake-category)) + ;; Now "paint" the overlay with all the other non-category + ;; properties. + (cl-loop + for (ov-prop . value) in + (append (reverse ; ensure ealier props override later ones + (flymake--lookup-type-property type 'flymake-overlay-control)) + (alist-get type flymake-diagnostic-types-alist)) + do (overlay-put ov ov-prop value)) ;; Now ensure some essential defaults are set ;; (cl-flet ((default-maybe (prop value) - (unless (or (plist-member (overlay-properties ov) prop) - (let ((cat (overlay-get ov - 'flymake-category))) - (and cat - (plist-member (symbol-plist cat) prop)))) - (overlay-put ov prop value)))) - (default-maybe 'bitmap 'flymake-error-bitmap) + (unless (plist-member (overlay-properties ov) prop) + (overlay-put ov prop (flymake--lookup-type-property + type prop value))))) (default-maybe 'face 'flymake-error) (default-maybe 'before-string (flymake--fringe-overlay-spec - (overlay-get ov 'bitmap))) + (flymake--lookup-type-property + type + 'flymake-bitmap + (alist-get 'bitmap (alist-get type ; backward compat + flymake-diagnostic-types-alist))))) (default-maybe 'help-echo (lambda (window _ov pos) (with-selected-window window @@ -818,7 +885,9 @@ The commands `flymake-goto-next-error' and diagnostics annotated in the buffer. The visual appearance of each type of diagnostic can be changed -in the variable `flymake-diagnostic-types-alist'. +by setting properties `flymake-overlay-control', `flymake-bitmap' +and `flymake-severity' on the symbols of diagnostic types (like +`:error', `:warning' and `:note'). Activation or deactivation of backends used by Flymake in each buffer happens via the special hook @@ -899,7 +968,7 @@ Do it only if `flymake-no-changes-timeout' is non-nil." (flymake--schedule-timer-maybe))) (defun flymake-after-save-hook () - (when flymake-mode + (when flymake-start-on-save-buffer (flymake-log :debug "starting syntax check as buffer was saved") (flymake-start t))) @@ -922,9 +991,9 @@ arg, skip any diagnostics with a severity less than `:warning'. If `flymake-wrap-around' is non-nil and no more next diagnostics, resumes search from top. -FILTER is a list of diagnostic types found in -`flymake-diagnostic-types-alist', or nil, if no filter is to be -applied." +FILTER is a list of diagnostic types. Only diagnostics with +matching severities matching are considered. If nil (the +default) no filter is applied." ;; TODO: let filter be a number, a severity below which diags are ;; skipped. (interactive (list 1 @@ -938,9 +1007,12 @@ applied." ov 'flymake-diagnostic))) (and diag - (or (not filter) - (memq (flymake--diag-type diag) - filter))))) + (or + (not filter) + (cl-find + (flymake--severity + (flymake--diag-type diag)) + filter :key #'flymake--severity))))) :compare (if (cl-plusp n) #'< #'>) :key #'overlay-start)) (tail (cl-member-if (lambda (ov) @@ -964,10 +1036,10 @@ applied." (funcall (overlay-get target 'help-echo) (selected-window) target (point))))) (interactive - (user-error "No more Flymake errors%s" + (user-error "No more Flymake diagnostics%s" (if filter - (format " of types %s" filter) - "")))))) + (format " of %s severity" + (mapconcat #'symbol-name filter ", ")) "")))))) (defun flymake-goto-prev-error (&optional n filter interactive) "Go to Nth previous Flymake diagnostic that matches FILTER. @@ -978,9 +1050,9 @@ prefix arg, skip any diagnostics with a severity less than If `flymake-wrap-around' is non-nil and no more previous diagnostics, resumes search from bottom. -FILTER is a list of diagnostic types found in -`flymake-diagnostic-types-alist', or nil, if no filter is to be -applied." +FILTER is a list of diagnostic types. Only diagnostics with +matching severities matching are considered. If nil (the +default) no filter is applied." (interactive (list 1 (if current-prefix-arg '(:error :warning)) t)) @@ -1063,12 +1135,10 @@ applied." (cl-loop for (type . severity) in (cl-sort (mapcar (lambda (type) - (cons type (flymake--lookup-type-property - type - 'severity - (warning-numeric-level :error)))) + (cons type (flymake--severity type))) (cl-union (hash-table-keys diags-by-type) - '(:error :warning))) + '(:error :warning) + :key #'flymake--severity)) #'> :key #'cdr) for diags = (gethash type diags-by-type) |