diff options
| -rw-r--r-- | lisp/progmodes/flymake-proc.el | 2 | ||||
| -rw-r--r-- | lisp/progmodes/flymake.el | 289 |
2 files changed, 198 insertions, 93 deletions
diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 0395fff3224..abda259e898 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el | |||
| @@ -409,7 +409,7 @@ Create parent directories as needed." | |||
| 409 | (string-to-number col-string)))) | 409 | (string-to-number col-string)))) |
| 410 | (with-current-buffer (process-buffer proc) | 410 | (with-current-buffer (process-buffer proc) |
| 411 | (push | 411 | (push |
| 412 | (flymake-ler-make | 412 | (flymake-make-diagnostic |
| 413 | :file fname | 413 | :file fname |
| 414 | :line line-number | 414 | :line line-number |
| 415 | :col col-number | 415 | :col col-number |
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index f00915a6846..72acc3a9204 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el | |||
| @@ -33,6 +33,8 @@ | |||
| 33 | ;;; Code: | 33 | ;;; Code: |
| 34 | 34 | ||
| 35 | (require 'cl-lib) | 35 | (require 'cl-lib) |
| 36 | (require 'thingatpt) ; end-of-thing | ||
| 37 | (require 'warnings) ; warning-numeric-level | ||
| 36 | 38 | ||
| 37 | (defgroup flymake nil | 39 | (defgroup flymake nil |
| 38 | "Universal on-the-fly syntax checker." | 40 | "Universal on-the-fly syntax checker." |
| @@ -136,57 +138,18 @@ are the string substitutions (see the function `format')." | |||
| 136 | (let* ((msg (apply #'format-message text args))) | 138 | (let* ((msg (apply #'format-message text args))) |
| 137 | (message "%s" msg)))) | 139 | (message "%s" msg)))) |
| 138 | 140 | ||
| 139 | (cl-defstruct (flymake-ler | 141 | (cl-defstruct (flymake--diag |
| 140 | (:constructor flymake-ler-make)) | 142 | (:constructor flymake-make-diagnostic)) |
| 141 | file line col type text full-file) | 143 | file line col type text full-file) |
| 142 | 144 | (define-obsolete-function-alias 'flymake-ler-make 'flymake-make-diagnostic "26.1" | |
| 143 | (defun flymake-ler-errorp (diag) | 145 | "Constructor for objects of type `flymake--diag'") |
| 144 | "Tell if DIAG is a flymake error or something else" | 146 | |
| 145 | (string= "e" (flymake-ler-type diag))) | 147 | (cl-defun flymake--overlays (&key beg end filter compare key) |
| 146 | 148 | "Get flymake-related overlays. | |
| 147 | (defun flymake--place-overlay (beg end tooltip-text face bitmap diag) | 149 | If BEG is non-nil and END is nil, consider only `overlays-at' |
| 148 | "Place a flymake overlay in range BEG and END. | 150 | BEG. Otherwise consider `overlays-in' the region comprised by BEG |
| 149 | Make a flymake fringe overlay for the line at BEG, if needed." | 151 | and END, defaulting to the whole buffer. Remove all that do not |
| 150 | (let* ((fringe-overlay | 152 | verify FILTER, sort them by COMPARE (using KEY)." |
| 151 | (or (cl-find-if (lambda (ov) | ||
| 152 | (overlay-get ov 'flymake--fringe-overlay)) | ||
| 153 | (overlays-at beg)) | ||
| 154 | (make-overlay beg (1+ beg))))) | ||
| 155 | (let ((ov fringe-overlay)) | ||
| 156 | (overlay-put ov 'help-echo | ||
| 157 | (concat tooltip-text "\n" | ||
| 158 | (overlay-get ov 'help-echo))) | ||
| 159 | (overlay-put ov 'before-string | ||
| 160 | (and flymake-fringe-indicator-position | ||
| 161 | (propertize "!" 'display | ||
| 162 | (cons flymake-fringe-indicator-position | ||
| 163 | (if (listp bitmap) | ||
| 164 | bitmap | ||
| 165 | (list bitmap)) | ||
| 166 | )))) | ||
| 167 | (overlay-put ov 'evaporate t) | ||
| 168 | (overlay-put ov 'flymake-overlay t) | ||
| 169 | (overlay-put ov 'priority 100) | ||
| 170 | ov) | ||
| 171 | (let ((ov (make-overlay beg end))) | ||
| 172 | (overlay-put ov 'face face) | ||
| 173 | (overlay-put ov 'help-echo | ||
| 174 | (concat tooltip-text "\n" | ||
| 175 | (overlay-get ov 'help-echo))) | ||
| 176 | (overlay-put ov 'evaporate t) | ||
| 177 | (overlay-put ov 'flymake-overlay t) | ||
| 178 | (overlay-put ov 'flymake--diagnostic diag)) | ||
| 179 | (cl-loop for i from 0 | ||
| 180 | for overlay in | ||
| 181 | (flymake--overlays | ||
| 182 | 'flymake--diagnostic | ||
| 183 | (lambda (_ov1 ov2) | ||
| 184 | (flymake-ler-errorp | ||
| 185 | (overlay-get ov2 'flymake--diagnostic))) | ||
| 186 | beg end) | ||
| 187 | do (overlay-put overlay 'priority (+ 100 i))))) | ||
| 188 | |||
| 189 | (defun flymake--overlays (&optional filter compare beg end) | ||
| 190 | (cl-remove-if-not | 153 | (cl-remove-if-not |
| 191 | (lambda (ov) | 154 | (lambda (ov) |
| 192 | (and (overlay-get ov 'flymake-overlay) | 155 | (and (overlay-get ov 'flymake-overlay) |
| @@ -195,12 +158,13 @@ Make a flymake fringe overlay for the line at BEG, if needed." | |||
| 195 | ((symbolp filter) (overlay-get ov filter)))))) | 158 | ((symbolp filter) (overlay-get ov filter)))))) |
| 196 | (save-restriction | 159 | (save-restriction |
| 197 | (widen) | 160 | (widen) |
| 198 | (let ((ovs (overlays-in (or beg (point-min)) | 161 | (let ((ovs (if (and beg (null end)) |
| 199 | (or end (point-max))))) | 162 | (overlays-at beg t) |
| 163 | (overlays-in (or beg (point-min)) | ||
| 164 | (or end (point-max)))))) | ||
| 200 | (if compare | 165 | (if compare |
| 201 | (cl-sort ovs | 166 | (cl-sort ovs compare :key (or key |
| 202 | compare | 167 | #'identity)) |
| 203 | :key #'overlay-start) | ||
| 204 | ovs))))) | 168 | ovs))))) |
| 205 | 169 | ||
| 206 | (defun flymake-delete-own-overlays () | 170 | (defun flymake-delete-own-overlays () |
| @@ -228,27 +192,167 @@ Make a flymake fringe overlay for the line at BEG, if needed." | |||
| 228 | (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") | 192 | (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") |
| 229 | (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") | 193 | (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") |
| 230 | 194 | ||
| 195 | (defun flymake--diag-region (diagnostic) | ||
| 196 | "Return the region (BEG . END) for DIAGNOSTIC. | ||
| 197 | Or nil if the region is invalid." | ||
| 198 | ;; FIXME: make this a generic function | ||
| 199 | (condition-case-unless-debug _err | ||
| 200 | (save-excursion | ||
| 201 | (goto-char (point-min)) | ||
| 202 | (let ((line (flymake--diag-line diagnostic)) | ||
| 203 | (col (flymake--diag-col diagnostic))) | ||
| 204 | (forward-line (1- line)) | ||
| 205 | (cl-flet ((fallback-bol | ||
| 206 | () (progn (back-to-indentation) (point))) | ||
| 207 | (fallback-eol | ||
| 208 | (beg) | ||
| 209 | (progn | ||
| 210 | (end-of-line) | ||
| 211 | (skip-chars-backward " \t\f\t\n" beg) | ||
| 212 | (if (eq (point) beg) | ||
| 213 | (line-beginning-position 2) | ||
| 214 | (point))))) | ||
| 215 | (if col | ||
| 216 | (let* ((beg (progn (forward-char (1- col)) (point))) | ||
| 217 | (sexp-end (ignore-errors (end-of-thing 'sexp))) | ||
| 218 | (end (or sexp-end | ||
| 219 | (fallback-eol beg)))) | ||
| 220 | (cons (if sexp-end beg (fallback-bol)) | ||
| 221 | end)) | ||
| 222 | (let* ((beg (fallback-bol)) | ||
| 223 | (end (fallback-eol beg))) | ||
| 224 | (cons beg end)))))) | ||
| 225 | (error (flymake-log 4 "Invalid region for diagnostic %s") | ||
| 226 | nil))) | ||
| 227 | |||
| 228 | (defvar flymake-diagnostic-types-alist | ||
| 229 | `((("e" :error error) | ||
| 230 | . ((flymake-category . flymake-error))) | ||
| 231 | (("w" :warning warning) | ||
| 232 | . ((flymake-category . flymake-warning)))) | ||
| 233 | "Alist ((KEY . PROPS)*) of properties of flymake error types. | ||
| 234 | KEY can be anything passed as `:type' to `flymake-diag-make', or | ||
| 235 | a list of these objects. | ||
| 236 | |||
| 237 | PROPS is an alist of properties that are applied, in order, to | ||
| 238 | the diagnostics of each type. The recognized properties are: | ||
| 239 | |||
| 240 | * Every property pertaining to overlays, except `category' and | ||
| 241 | `evaporate' (see Info Node `(elisp)Overlay Properties'), used | ||
| 242 | affect the appearance of Flymake annotations. | ||
| 243 | |||
| 244 | * `bitmap', an image displayed in the fringe according to | ||
| 245 | `flymake-fringe-indicator-position'. The value actually | ||
| 246 | follows the syntax of `flymake-error-bitmap' (which see). It | ||
| 247 | is overriden by any `before-string' overlay property. | ||
| 248 | |||
| 249 | * `severity', a non-negative integer specifying the diagnostic's | ||
| 250 | severity. The higher, the more serious. If the overlay | ||
| 251 | priority `priority' is not specified, `severity' is used to set | ||
| 252 | it and help sort overlapping overlays. | ||
| 253 | |||
| 254 | * `flymake-category', a symbol whose property list is considered | ||
| 255 | as a default for missing values of any other properties. This | ||
| 256 | is useful to backend authors when creating new diagnostic types | ||
| 257 | that differ from an existing type by only a few properties.") | ||
| 258 | |||
| 259 | (put 'flymake-error 'face 'flymake-error) | ||
| 260 | (put 'flymake-error 'bitmap flymake-error-bitmap) | ||
| 261 | (put 'flymake-error 'severity (warning-numeric-level :error)) | ||
| 262 | (put 'flymake-error 'mode-line-face 'compilation-error) | ||
| 263 | |||
| 264 | (put 'flymake-warning 'face 'flymake-warning) | ||
| 265 | (put 'flymake-warning 'bitmap flymake-warning-bitmap) | ||
| 266 | (put 'flymake-warning 'severity (warning-numeric-level :warning)) | ||
| 267 | (put 'flymake-warning 'mode-line-face 'compilation-warning) | ||
| 268 | |||
| 269 | (put 'flymake-note 'face 'flymake-note) | ||
| 270 | (put 'flymake-note 'bitmap flymake-warning-bitmap) | ||
| 271 | (put 'flymake-note 'severity (warning-numeric-level :debug)) | ||
| 272 | (put 'flymake-note 'mode-line-face 'compilation-info) | ||
| 273 | |||
| 274 | (defun flymake--lookup-type-property (type prop &optional default) | ||
| 275 | "Look up PROP for TYPE in `flymake-diagnostic-types-alist'. | ||
| 276 | If TYPE doesn't declare PROP in either | ||
| 277 | `flymake-diagnostic-types-alist' or its associated category, | ||
| 278 | return DEFAULT." | ||
| 279 | (let ((alist-probe (assoc type flymake-diagnostic-types-alist | ||
| 280 | (lambda (entry key) | ||
| 281 | (or (equal key entry) | ||
| 282 | (member key entry)))))) | ||
| 283 | (cond (alist-probe | ||
| 284 | (let* ((alist (cdr alist-probe)) | ||
| 285 | (prop-probe (assoc prop alist))) | ||
| 286 | (if prop-probe | ||
| 287 | (cdr prop-probe) | ||
| 288 | (if-let* ((cat (assoc-default 'flymake-category alist)) | ||
| 289 | (plist (and (symbolp cat) | ||
| 290 | (symbol-plist cat))) | ||
| 291 | (cat-probe (plist-member plist prop))) | ||
| 292 | (cadr cat-probe) | ||
| 293 | default)))) | ||
| 294 | (t | ||
| 295 | default)))) | ||
| 296 | |||
| 297 | (defun flymake--diag-errorp (diag) | ||
| 298 | "Tell if DIAG is a flymake error or something else" | ||
| 299 | (let ((sev (flymake--lookup-type-property 'severity | ||
| 300 | (flymake--diag-type diag) | ||
| 301 | (warning-numeric-level :error)))) | ||
| 302 | (>= sev (warning-numeric-level :error)))) | ||
| 303 | |||
| 304 | (defun flymake--fringe-overlay-spec (bitmap) | ||
| 305 | (and flymake-fringe-indicator-position | ||
| 306 | bitmap | ||
| 307 | (propertize "!" 'display | ||
| 308 | (cons flymake-fringe-indicator-position | ||
| 309 | (if (listp bitmap) | ||
| 310 | bitmap | ||
| 311 | (list bitmap)))))) | ||
| 312 | |||
| 231 | (defun flymake--highlight-line (diagnostic) | 313 | (defun flymake--highlight-line (diagnostic) |
| 232 | "Highlight buffer with info in DIAGNOSTIC. | 314 | "Highlight buffer with info in DIAGNOSTIC." |
| 233 | Reuse overlays if necessary | 315 | (when-let* ((region (flymake--diag-region diagnostic)) |
| 234 | Perhaps use the message text as a hint to enhance highlighting." | 316 | (ov (make-overlay (car region) (cdr region)))) |
| 235 | (save-excursion | 317 | ;; First set `category' in the overlay, then copy over every other |
| 236 | (goto-char (point-min)) | 318 | ;; property. |
| 237 | (let ((line-no (flymake-ler-line diagnostic))) | 319 | ;; |
| 238 | (forward-line (1- line-no)) | 320 | (let ((alist (assoc-default (flymake--diag-type diagnostic) |
| 239 | (pcase-let* ((beg (progn (back-to-indentation) (point))) | 321 | flymake-diagnostic-types-alist))) |
| 240 | (end (progn | 322 | (overlay-put ov 'category (assoc-default 'flymake-category alist)) |
| 241 | (end-of-line) | 323 | (cl-loop for (k . v) in alist |
| 242 | (skip-chars-backward " \t\f\t\n" beg) | 324 | unless (eq k 'category) |
| 243 | (if (eq (point) beg) | 325 | do (overlay-put ov k v))) |
| 244 | (line-beginning-position 2) | 326 | ;; Now ensure some essential defaults are set |
| 245 | (point)))) | 327 | ;; |
| 246 | (tooltip-text (flymake-ler-text diagnostic)) | 328 | (cl-flet ((default-maybe |
| 247 | (`(,face ,bitmap) | 329 | (prop value) |
| 248 | (if (equal "e" (flymake-ler-type diagnostic)) | 330 | (unless (or (plist-member (overlay-properties ov) prop) |
| 249 | (list 'flymake-errline flymake-error-bitmap) | 331 | (let ((cat (overlay-get ov |
| 250 | (list 'flymake-warnline flymake-warning-bitmap)))) | 332 | 'flymake-category))) |
| 251 | (flymake--place-overlay beg end tooltip-text face bitmap diagnostic))))) | 333 | (and cat |
| 334 | (plist-member (symbol-plist cat) prop)))) | ||
| 335 | (overlay-put ov prop value)))) | ||
| 336 | (default-maybe 'bitmap flymake-error-bitmap) | ||
| 337 | (default-maybe 'before-string | ||
| 338 | (flymake--fringe-overlay-spec | ||
| 339 | (overlay-get ov 'bitmap))) | ||
| 340 | (default-maybe 'help-echo | ||
| 341 | (lambda (_window _ov pos) | ||
| 342 | (mapconcat | ||
| 343 | (lambda (ov) | ||
| 344 | (let ((diag (overlay-get ov 'flymake--diagnostic))) | ||
| 345 | (flymake--diag-text diag))) | ||
| 346 | (flymake--overlays :beg pos) | ||
| 347 | "\n"))) | ||
| 348 | (default-maybe 'severity (warning-numeric-level :error)) | ||
| 349 | (default-maybe 'priority (+ 100 (overlay-get ov 'severity)))) | ||
| 350 | ;; Some properties can't be overriden | ||
| 351 | ;; | ||
| 352 | (overlay-put ov 'evaporate t) | ||
| 353 | (overlay-put ov 'flymake-overlay t) | ||
| 354 | (overlay-put ov 'flymake--diagnostic diagnostic))) | ||
| 355 | |||
| 252 | 356 | ||
| 253 | (defvar-local flymake-is-running nil | 357 | (defvar-local flymake-is-running nil |
| 254 | "If t, flymake syntax check process is running for the current buffer.") | 358 | "If t, flymake syntax check process is running for the current buffer.") |
| @@ -273,17 +377,17 @@ Perhaps use the message text as a hint to enhance highlighting." | |||
| 273 | "Pop up a menu with errors/warnings for current line." | 377 | "Pop up a menu with errors/warnings for current line." |
| 274 | (interactive (list last-nonmenu-event)) | 378 | (interactive (list last-nonmenu-event)) |
| 275 | (let* ((diag-overlays (or | 379 | (let* ((diag-overlays (or |
| 276 | (flymake--overlays 'flymake--diagnostic nil | 380 | (flymake--overlays :filter 'flymake--diagnostic |
| 277 | (line-beginning-position) | 381 | :beg (line-beginning-position) |
| 278 | (line-end-position)) | 382 | :end (line-end-position)) |
| 279 | (user-error "No flymake problem for current line"))) | 383 | (user-error "No flymake problem for current line"))) |
| 280 | (menu (mapcar (lambda (ov) | 384 | (menu (mapcar (lambda (ov) |
| 281 | (let ((diag (overlay-get ov 'flymake--diagnostic))) | 385 | (let ((diag (overlay-get ov 'flymake--diagnostic))) |
| 282 | (cons (format "%s - %s(%s)" | 386 | (cons (format "%s - %s(%s)" |
| 283 | (flymake-ler-text diag) | 387 | (flymake--diag-text diag) |
| 284 | (or (flymake-ler-file diag) | 388 | (or (flymake--diag-file diag) |
| 285 | "(no file)") | 389 | "(no file)") |
| 286 | (or (flymake-ler-line diag) | 390 | (or (flymake--diag-line diag) |
| 287 | "?")) | 391 | "?")) |
| 288 | ov))) | 392 | ov))) |
| 289 | diag-overlays)) | 393 | diag-overlays)) |
| @@ -294,8 +398,8 @@ Perhaps use the message text as a hint to enhance highlighting." | |||
| 294 | diag-overlays)) | 398 | diag-overlays)) |
| 295 | (title (format "Line %d: %d error(s), %d other(s)" | 399 | (title (format "Line %d: %d error(s), %d other(s)" |
| 296 | (line-number-at-pos) | 400 | (line-number-at-pos) |
| 297 | (cl-count-if #'flymake-ler-errorp diagnostics) | 401 | (cl-count-if #'flymake--diag-errorp diagnostics) |
| 298 | (cl-count-if-not #'flymake-ler-errorp diagnostics))) | 402 | (cl-count-if-not #'flymake--diag-errorp diagnostics))) |
| 299 | (choice (x-popup-menu event (list title (cons "" menu))))) | 403 | (choice (x-popup-menu event (list title (cons "" menu))))) |
| 300 | (flymake-log 3 "choice=%s" choice) | 404 | (flymake-log 3 "choice=%s" choice) |
| 301 | ;; FIXME: What is the point of going to the problem locus if we're | 405 | ;; FIXME: What is the point of going to the problem locus if we're |
| @@ -338,8 +442,8 @@ Perhaps use the message text as a hint to enhance highlighting." | |||
| 338 | 442 | ||
| 339 | (defun flymake--fix-line-numbers (diagnostic) | 443 | (defun flymake--fix-line-numbers (diagnostic) |
| 340 | "Ensure DIAGNOSTIC has sensible error lines" | 444 | "Ensure DIAGNOSTIC has sensible error lines" |
| 341 | (setf (flymake-ler-line diagnostic) | 445 | (setf (flymake--diag-line diagnostic) |
| 342 | (min (max (flymake-ler-line diagnostic) | 446 | (min (max (flymake--diag-line diagnostic) |
| 343 | 1) | 447 | 1) |
| 344 | (line-number-at-pos (point-max) 'absolute)))) | 448 | (line-number-at-pos (point-max) 'absolute)))) |
| 345 | 449 | ||
| @@ -349,8 +453,8 @@ Perhaps use the message text as a hint to enhance highlighting." | |||
| 349 | (flymake-delete-own-overlays) | 453 | (flymake-delete-own-overlays) |
| 350 | (mapc #'flymake--fix-line-numbers diagnostics) | 454 | (mapc #'flymake--fix-line-numbers diagnostics) |
| 351 | (mapc #'flymake--highlight-line diagnostics) | 455 | (mapc #'flymake--highlight-line diagnostics) |
| 352 | (let ((err-count (cl-count-if #'flymake-ler-errorp diagnostics)) | 456 | (let ((err-count (cl-count-if #'flymake--diag-errorp diagnostics)) |
| 353 | (warn-count (cl-count-if-not #'flymake-ler-errorp diagnostics))) | 457 | (warn-count (cl-count-if-not #'flymake--diag-errorp diagnostics))) |
| 354 | (when flymake-check-start-time | 458 | (when flymake-check-start-time |
| 355 | (flymake-log 2 "%s: %d error(s), %d other(s) in %.2f second(s)" | 459 | (flymake-log 2 "%s: %d error(s), %d other(s) in %.2f second(s)" |
| 356 | (buffer-name) err-count warn-count | 460 | (buffer-name) err-count warn-count |
| @@ -447,11 +551,12 @@ Perhaps use the message text as a hint to enhance highlighting." | |||
| 447 | "Go to next, or Nth next, flymake error in buffer." | 551 | "Go to next, or Nth next, flymake error in buffer." |
| 448 | (interactive (list 1 t)) | 552 | (interactive (list 1 t)) |
| 449 | (let* ((n (or n 1)) | 553 | (let* ((n (or n 1)) |
| 450 | (ovs (flymake--overlays 'flymake--diagnostic | 554 | (ovs (flymake--overlays :filter 'flymake--diagnostic |
| 451 | (if (cl-plusp n) #'< #'>))) | 555 | :compare (if (cl-plusp n) #'< #'>) |
| 452 | (chain (cl-member-if (lambda (ov) | 556 | :key #'overlay-start)) |
| 453 | (if (cl-plusp n) | 557 | (chain (cl-member-if (lambda (ov) |
| 454 | (> (overlay-start ov) | 558 | (if (cl-plusp n) |
| 559 | (> (overlay-start ov) | ||
| 455 | (point)) | 560 | (point)) |
| 456 | (< (overlay-start ov) | 561 | (< (overlay-start ov) |
| 457 | (point)))) | 562 | (point)))) |