aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoão Távora2017-09-27 22:35:49 +0100
committerJoão Távora2017-09-28 08:09:49 +0100
commitce540f8a687672fade6eb91e64ddf86e1e868784 (patch)
treeb583f16a5925900802e56292af0a276a3c57d04c
parent7cf59c6635f747fe2d974b92be1fd937d3821681 (diff)
downloademacs-ce540f8a687672fade6eb91e64ddf86e1e868784.tar.gz
emacs-ce540f8a687672fade6eb91e64ddf86e1e868784.zip
Revert "Split flymake.el into flymake-proc.el and flymake-ui.el"
In other words, re-coalesce the two files, lisp/progmodes/flymake-proc.el and lisp/progmodes/flymake-ui.el, back into a single one, lisp/progmodes/flymake.el. The changesets "Prefer HTTPS to FTP and HTTP in documentation" and "allow nil init in flymake-allowed-file-name-masks to disable flymake" are kept in place in the new lisp/progmodes/flymake.el. This reverts Git commit eb34f7f5a29e7bf62326ecb6e693f28878be28cd. Don't merge this back to master as development happening there builds upon this work. See also https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00932.html.
-rw-r--r--lisp/progmodes/flymake-proc.el1091
-rw-r--r--lisp/progmodes/flymake-ui.el604
-rw-r--r--lisp/progmodes/flymake.el1629
3 files changed, 1620 insertions, 1704 deletions
diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el
deleted file mode 100644
index 0cbf3e1c67d..00000000000
--- a/lisp/progmodes/flymake-proc.el
+++ /dev/null
@@ -1,1091 +0,0 @@
1;;; flymake-proc.el --- Flymake for external syntax checker processes -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2003-2017 Free Software Foundation, Inc.
4
5;; Author: Pavel Kobyakov <pk_at_work@yahoo.com>
6;; Maintainer: Leo Liu <sdl.web@gmail.com>
7;; Version: 0.3
8;; Keywords: c languages tools
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
24
25;;; Commentary:
26;;
27;; Flymake is a minor Emacs mode performing on-the-fly syntax checks.
28;;
29;; This file contains the most original implementation of flymake's
30;; main source of on-the-fly diagnostic info, the external syntax
31;; checker backend.
32;;
33;;; Bugs/todo:
34
35;; - Only uses "Makefile", not "makefile" or "GNUmakefile"
36;; (from http://bugs.debian.org/337339).
37
38;;; Code:
39
40(require 'flymake-ui)
41
42(defcustom flymake-compilation-prevents-syntax-check t
43 "If non-nil, don't start syntax check if compilation is running."
44 :group 'flymake
45 :type 'boolean)
46
47(defcustom flymake-xml-program
48 (if (executable-find "xmlstarlet") "xmlstarlet" "xml")
49 "Program to use for XML validation."
50 :type 'file
51 :group 'flymake
52 :version "24.4")
53
54(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest")
55 "Dirs where to look for master files."
56 :group 'flymake
57 :type '(repeat (string)))
58
59(defcustom flymake-master-file-count-limit 32
60 "Max number of master files to check."
61 :group 'flymake
62 :type 'integer)
63
64(defcustom flymake-allowed-file-name-masks
65 '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-simple-make-init)
66 ("\\.xml\\'" flymake-xml-init)
67 ("\\.html?\\'" flymake-xml-init)
68 ("\\.cs\\'" flymake-simple-make-init)
69 ("\\.p[ml]\\'" flymake-perl-init)
70 ("\\.php[345]?\\'" flymake-php-init)
71 ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup)
72 ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup)
73 ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup)
74 ("\\.tex\\'" flymake-simple-tex-init)
75 ("\\.idl\\'" flymake-simple-make-init)
76 ;; ("\\.cpp\\'" 1)
77 ;; ("\\.java\\'" 3)
78 ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'")
79 ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2))
80 ;; ("\\.idl\\'" 1)
81 ;; ("\\.odl\\'" 1)
82 ;; ("[0-9]+\\.tex\\'" 2 ("\\.tex\\'")
83 ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 ))
84 ;; ("\\.tex\\'" 1)
85 )
86 "Files syntax checking is allowed for.
87This is an alist with elements of the form:
88 REGEXP [INIT [CLEANUP [NAME]]]
89REGEXP is a regular expression that matches a file name.
90INIT is the init function to use, missing means disable `flymake-mode'.
91CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'.
92NAME is the file name function to use, default `flymake-get-real-file-name'."
93 :group 'flymake
94 :type '(alist :key-type (regexp :tag "File regexp")
95 :value-type
96 (list :tag "Handler functions"
97 (choice :tag "Init function"
98 (const :tag "disable" nil)
99 function)
100 (choice :tag "Cleanup function"
101 (const :tag "flymake-simple-cleanup" nil)
102 function)
103 (choice :tag "Name function"
104 (const :tag "flymake-get-real-file-name" nil)
105 function))))
106
107(defvar flymake-processes nil
108 "List of currently active flymake processes.")
109
110(defvar-local flymake-output-residual nil)
111
112(defun flymake-get-file-name-mode-and-masks (file-name)
113 "Return the corresponding entry from `flymake-allowed-file-name-masks'."
114 (unless (stringp file-name)
115 (error "Invalid file-name"))
116 (let ((fnm flymake-allowed-file-name-masks)
117 (mode-and-masks nil))
118 (while (and (not mode-and-masks) fnm)
119 (let ((item (pop fnm)))
120 (when (string-match (car item) file-name)
121 (setq mode-and-masks item)))) ; (cdr item) may be nil
122 (setq mode-and-masks (cdr mode-and-masks))
123 (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks))
124 mode-and-masks))
125
126(defun flymake-can-syntax-check-file (file-name)
127 "Determine whether we can syntax check FILE-NAME.
128Return nil if we cannot, non-nil if we can."
129 (if (flymake-get-init-function file-name) t nil))
130
131(defun flymake-get-init-function (file-name)
132 "Return init function to be used for the file."
133 (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name))))
134 ;;(flymake-log 0 "calling %s" init-f)
135 ;;(funcall init-f (current-buffer))
136 init-f))
137
138(defun flymake-get-cleanup-function (file-name)
139 "Return cleanup function to be used for the file."
140 (or (nth 1 (flymake-get-file-name-mode-and-masks file-name))
141 'flymake-simple-cleanup))
142
143(defun flymake-get-real-file-name-function (file-name)
144 (or (nth 2 (flymake-get-file-name-mode-and-masks file-name))
145 'flymake-get-real-file-name))
146
147(defvar flymake-find-buildfile-cache (make-hash-table :test #'equal))
148
149(defun flymake-get-buildfile-from-cache (dir-name)
150 "Look up DIR-NAME in cache and return its associated value.
151If DIR-NAME is not found, return nil."
152 (gethash dir-name flymake-find-buildfile-cache))
153
154(defun flymake-add-buildfile-to-cache (dir-name buildfile)
155 "Associate DIR-NAME with BUILDFILE in the buildfile cache."
156 (puthash dir-name buildfile flymake-find-buildfile-cache))
157
158(defun flymake-clear-buildfile-cache ()
159 "Clear the buildfile cache."
160 (clrhash flymake-find-buildfile-cache))
161
162(defun flymake-find-buildfile (buildfile-name source-dir-name)
163 "Find buildfile starting from current directory.
164Buildfile includes Makefile, build.xml etc.
165Return its file name if found, or nil if not found."
166 (or (flymake-get-buildfile-from-cache source-dir-name)
167 (let* ((file (locate-dominating-file source-dir-name buildfile-name)))
168 (if file
169 (progn
170 (flymake-log 3 "found buildfile at %s" file)
171 (flymake-add-buildfile-to-cache source-dir-name file)
172 file)
173 (progn
174 (flymake-log 3 "buildfile for %s not found" source-dir-name)
175 nil)))))
176
177(defun flymake-fix-file-name (name)
178 "Replace all occurrences of `\\' with `/'."
179 (when name
180 (setq name (expand-file-name name))
181 (setq name (abbreviate-file-name name))
182 (setq name (directory-file-name name))
183 name))
184
185(defun flymake-same-files (file-name-one file-name-two)
186 "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file.
187Return t if so, nil if not."
188 (equal (flymake-fix-file-name file-name-one)
189 (flymake-fix-file-name file-name-two)))
190
191;; This is bound dynamically to pass a parameter to a sort predicate below
192(defvar flymake-included-file-name)
193
194(defun flymake-find-possible-master-files (file-name master-file-dirs masks)
195 "Find (by name and location) all possible master files.
196
197Name is specified by FILE-NAME and location is specified by
198MASTER-FILE-DIRS. Master files include .cpp and .c for .h.
199Files are searched for starting from the .h directory and max
200max-level parent dirs. File contents are not checked."
201 (let* ((dirs master-file-dirs)
202 (files nil)
203 (done nil))
204
205 (while (and (not done) dirs)
206 (let* ((dir (expand-file-name (car dirs) (file-name-directory file-name)))
207 (masks masks))
208 (while (and (file-exists-p dir) (not done) masks)
209 (let* ((mask (car masks))
210 (dir-files (directory-files dir t mask)))
211
212 (flymake-log 3 "dir %s, %d file(s) for mask %s"
213 dir (length dir-files) mask)
214 (while (and (not done) dir-files)
215 (when (not (file-directory-p (car dir-files)))
216 (setq files (cons (car dir-files) files))
217 (when (>= (length files) flymake-master-file-count-limit)
218 (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit)
219 (setq done t)))
220 (setq dir-files (cdr dir-files))))
221 (setq masks (cdr masks))))
222 (setq dirs (cdr dirs)))
223 (when files
224 (let ((flymake-included-file-name (file-name-nondirectory file-name)))
225 (setq files (sort files 'flymake-master-file-compare))))
226 (flymake-log 3 "found %d possible master file(s)" (length files))
227 files))
228
229(defun flymake-master-file-compare (file-one file-two)
230 "Compare two files specified by FILE-ONE and FILE-TWO.
231This function is used in sort to move most possible file names
232to the beginning of the list (File.h -> File.cpp moved to top)."
233 (and (equal (file-name-sans-extension flymake-included-file-name)
234 (file-name-base file-one))
235 (not (equal file-one file-two))))
236
237(defvar flymake-check-file-limit 8192
238 "Maximum number of chars to look at when checking possible master file.
239Nil means search the entire file.")
240
241(defun flymake-check-patch-master-file-buffer
242 (master-file-temp-buffer
243 master-file-name patched-master-file-name
244 source-file-name patched-source-file-name
245 include-dirs regexp)
246 "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME.
247If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME
248instead of SOURCE-FILE-NAME.
249
250For example, foo.cpp is a master file if it includes foo.h.
251
252When a buffer for MASTER-FILE-NAME exists, use it as a source
253instead of reading master file from disk."
254 (let* ((source-file-nondir (file-name-nondirectory source-file-name))
255 (source-file-extension (file-name-extension source-file-nondir))
256 (source-file-nonext (file-name-sans-extension source-file-nondir))
257 (found nil)
258 (inc-name nil)
259 (search-limit flymake-check-file-limit))
260 (setq regexp
261 (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\""
262 ;; Hack for tex files, where \include often excludes .tex.
263 ;; Maybe this is safe generally.
264 (if (and (> (length source-file-extension) 1)
265 (string-equal source-file-extension "tex"))
266 (format "%s\\(?:\\.%s\\)?"
267 (regexp-quote source-file-nonext)
268 (regexp-quote source-file-extension))
269 (regexp-quote source-file-nondir))))
270 (unwind-protect
271 (with-current-buffer master-file-temp-buffer
272 (if (or (not search-limit)
273 (> search-limit (point-max)))
274 (setq search-limit (point-max)))
275 (flymake-log 3 "checking %s against regexp %s"
276 master-file-name regexp)
277 (goto-char (point-min))
278 (while (and (< (point) search-limit)
279 (re-search-forward regexp search-limit t))
280 (let ((match-beg (match-beginning 1))
281 (match-end (match-end 1)))
282
283 (flymake-log 3 "found possible match for %s" source-file-nondir)
284 (setq inc-name (match-string 1))
285 (and (> (length source-file-extension) 1)
286 (string-equal source-file-extension "tex")
287 (not (string-match (format "\\.%s\\'" source-file-extension)
288 inc-name))
289 (setq inc-name (concat inc-name "." source-file-extension)))
290 (when (eq t (compare-strings
291 source-file-nondir nil nil
292 inc-name (- (length inc-name)
293 (length source-file-nondir)) nil))
294 (flymake-log 3 "inc-name=%s" inc-name)
295 (when (flymake-check-include source-file-name inc-name
296 include-dirs)
297 (setq found t)
298 ;; replace-match is not used here as it fails in
299 ;; XEmacs with 'last match not a buffer' error as
300 ;; check-includes calls replace-in-string
301 (flymake-replace-region
302 match-beg match-end
303 (file-name-nondirectory patched-source-file-name))))
304 (forward-line 1)))
305 (when found
306 (flymake-save-buffer-in-file patched-master-file-name)))
307 ;;+(flymake-log 3 "killing buffer %s"
308 ;; (buffer-name master-file-temp-buffer))
309 (kill-buffer master-file-temp-buffer))
310 ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found)
311 (when found
312 (flymake-log 2 "found master file %s" master-file-name))
313 found))
314
315;;; XXX: remove
316(defun flymake-replace-region (beg end rep)
317 "Replace text in BUFFER in region (BEG END) with REP."
318 (save-excursion
319 (goto-char end)
320 ;; Insert before deleting, so as to better preserve markers's positions.
321 (insert rep)
322 (delete-region beg end)))
323
324(defun flymake-read-file-to-temp-buffer (file-name)
325 "Insert contents of FILE-NAME into newly created temp buffer."
326 (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name))))))
327 (with-current-buffer temp-buffer
328 (insert-file-contents file-name))
329 temp-buffer))
330
331(defun flymake-copy-buffer-to-temp-buffer (buffer)
332 "Copy contents of BUFFER into newly created temp buffer."
333 (with-current-buffer
334 (get-buffer-create (generate-new-buffer-name
335 (concat "flymake:" (buffer-name buffer))))
336 (insert-buffer-substring buffer)
337 (current-buffer)))
338
339(defun flymake-check-include (source-file-name inc-name include-dirs)
340 "Check if SOURCE-FILE-NAME can be found in include path.
341Return t if it can be found via include path using INC-NAME."
342 (if (file-name-absolute-p inc-name)
343 (flymake-same-files source-file-name inc-name)
344 (while (and include-dirs
345 (not (flymake-same-files
346 source-file-name
347 (concat (file-name-directory source-file-name)
348 "/" (car include-dirs)
349 "/" inc-name))))
350 (setq include-dirs (cdr include-dirs)))
351 include-dirs))
352
353(defun flymake-find-buffer-for-file (file-name)
354 "Check if there exists a buffer visiting FILE-NAME.
355Return t if so, nil if not."
356 (let ((buffer-name (get-file-buffer file-name)))
357 (if buffer-name
358 (get-buffer buffer-name))))
359
360(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp)
361 "Save SOURCE-FILE-NAME with a different name.
362Find master file, patch and save it."
363 (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks))
364 (master-file-count (length possible-master-files))
365 (idx 0)
366 (temp-buffer nil)
367 (master-file-name nil)
368 (patched-master-file-name nil)
369 (found nil))
370
371 (while (and (not found) (< idx master-file-count))
372 (setq master-file-name (nth idx possible-master-files))
373 (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master"))
374 (if (flymake-find-buffer-for-file master-file-name)
375 (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name)))
376 (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name)))
377 (setq found
378 (flymake-check-patch-master-file-buffer
379 temp-buffer
380 master-file-name
381 patched-master-file-name
382 source-file-name
383 patched-source-file-name
384 (funcall get-incl-dirs-f (file-name-directory master-file-name))
385 include-regexp))
386 (setq idx (1+ idx)))
387 (if found
388 (list master-file-name patched-master-file-name)
389 (progn
390 (flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count
391 (file-name-nondirectory source-file-name))
392 nil))))
393
394(defun flymake-save-buffer-in-file (file-name)
395 "Save the entire buffer contents into file FILE-NAME.
396Create parent directories as needed."
397 (make-directory (file-name-directory file-name) 1)
398 (write-region nil nil file-name nil 566)
399 (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name))
400
401(defun flymake-process-filter (process output)
402 "Parse OUTPUT and highlight error lines.
403It's flymake process filter."
404 (let ((source-buffer (process-buffer process)))
405
406 (flymake-log 3 "received %d byte(s) of output from process %d"
407 (length output) (process-id process))
408 (when (buffer-live-p source-buffer)
409 (with-current-buffer source-buffer
410 (flymake-parse-output-and-residual output)))))
411
412(defun flymake-process-sentinel (process _event)
413 "Sentinel for syntax check buffers."
414 (when (memq (process-status process) '(signal exit))
415 (let* ((exit-status (process-exit-status process))
416 (command (process-command process))
417 (source-buffer (process-buffer process))
418 (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer))))
419
420 (flymake-log 2 "process %d exited with code %d"
421 (process-id process) exit-status)
422 (condition-case err
423 (progn
424 (flymake-log 3 "cleaning up using %s" cleanup-f)
425 (when (buffer-live-p source-buffer)
426 (with-current-buffer source-buffer
427 (funcall cleanup-f)))
428
429 (delete-process process)
430 (setq flymake-processes (delq process flymake-processes))
431
432 (when (buffer-live-p source-buffer)
433 (with-current-buffer source-buffer
434
435 (flymake-parse-residual)
436 (flymake-post-syntax-check exit-status command)
437 (setq flymake-is-running nil))))
438 (error
439 (let ((err-str (format "Error in process sentinel for buffer %s: %s"
440 source-buffer (error-message-string err))))
441 (flymake-log 0 err-str)
442 (with-current-buffer source-buffer
443 (setq flymake-is-running nil))))))))
444
445(defun flymake-post-syntax-check (exit-status command)
446 (save-restriction
447 (widen)
448 (setq flymake-err-info flymake-new-err-info)
449 (setq flymake-new-err-info nil)
450 (setq flymake-err-info
451 (flymake-fix-line-numbers
452 flymake-err-info 1 (count-lines (point-min) (point-max))))
453 (flymake-delete-own-overlays)
454 (flymake-highlight-err-lines flymake-err-info)
455 (let (err-count warn-count)
456 (setq err-count (flymake-get-err-count flymake-err-info "e"))
457 (setq warn-count (flymake-get-err-count flymake-err-info "w"))
458 (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)"
459 (buffer-name) err-count warn-count
460 (- (float-time) flymake-check-start-time))
461 (setq flymake-check-start-time nil)
462
463 (if (and (equal 0 err-count) (equal 0 warn-count))
464 (if (equal 0 exit-status)
465 (flymake-report-status "" "") ; PASSED
466 (if (not flymake-check-was-interrupted)
467 (flymake-report-fatal-status "CFGERR"
468 (format "Configuration error has occurred while running %s" command))
469 (flymake-report-status nil ""))) ; "STOPPED"
470 (flymake-report-status (format "%d/%d" err-count warn-count) "")))))
471
472(defun flymake-parse-output-and-residual (output)
473 "Split OUTPUT into lines, merge in residual if necessary."
474 (let* ((buffer-residual flymake-output-residual)
475 (total-output (if buffer-residual (concat buffer-residual output) output))
476 (lines-and-residual (flymake-split-output total-output))
477 (lines (nth 0 lines-and-residual))
478 (new-residual (nth 1 lines-and-residual)))
479 (setq flymake-output-residual new-residual)
480 (setq flymake-new-err-info
481 (flymake-parse-err-lines
482 flymake-new-err-info lines))))
483
484(defun flymake-parse-residual ()
485 "Parse residual if it's non empty."
486 (when flymake-output-residual
487 (setq flymake-new-err-info
488 (flymake-parse-err-lines
489 flymake-new-err-info
490 (list flymake-output-residual)))
491 (setq flymake-output-residual nil)))
492
493(defun flymake-fix-line-numbers (err-info-list min-line max-line)
494 "Replace line numbers with fixed value.
495If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE.
496If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE.
497The reason for this fix is because some compilers might report
498line number outside the file being compiled."
499 (let* ((count (length err-info-list))
500 (err-info nil)
501 (line 0))
502 (while (> count 0)
503 (setq err-info (nth (1- count) err-info-list))
504 (setq line (flymake-er-get-line err-info))
505 (when (or (< line min-line) (> line max-line))
506 (setq line (if (< line min-line) min-line max-line))
507 (setq err-info-list (flymake-set-at err-info-list (1- count)
508 (flymake-er-make-er line
509 (flymake-er-get-line-err-info-list err-info)))))
510 (setq count (1- count))))
511 err-info-list)
512
513(defun flymake-parse-err-lines (err-info-list lines)
514 "Parse err LINES, store info in ERR-INFO-LIST."
515 (let* ((count (length lines))
516 (idx 0)
517 (line-err-info nil)
518 (real-file-name nil)
519 (source-file-name buffer-file-name)
520 (get-real-file-name-f (flymake-get-real-file-name-function source-file-name)))
521
522 (while (< idx count)
523 (setq line-err-info (flymake-parse-line (nth idx lines)))
524 (when line-err-info
525 (setq real-file-name (funcall get-real-file-name-f
526 (flymake-ler-file line-err-info)))
527 (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name))
528
529 (when (flymake-same-files real-file-name source-file-name)
530 (setq line-err-info (flymake-ler-set-file line-err-info nil))
531 (setq err-info-list (flymake-add-err-info err-info-list line-err-info))))
532 (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no"))
533 (setq idx (1+ idx)))
534 err-info-list))
535
536(defun flymake-split-output (output)
537 "Split OUTPUT into lines.
538Return last one as residual if it does not end with newline char.
539Returns ((LINES) RESIDUAL)."
540 (when (and output (> (length output) 0))
541 (let* ((lines (split-string output "[\n\r]+" t))
542 (complete (equal "\n" (char-to-string (aref output (1- (length output))))))
543 (residual nil))
544 (when (not complete)
545 (setq residual (car (last lines)))
546 (setq lines (butlast lines)))
547 (list lines residual))))
548
549(defun flymake-reformat-err-line-patterns-from-compile-el (original-list)
550 "Grab error line patterns from ORIGINAL-LIST in compile.el format.
551Convert it to flymake internal format."
552 (let* ((converted-list '()))
553 (dolist (item original-list)
554 (setq item (cdr item))
555 (let ((regexp (nth 0 item))
556 (file (nth 1 item))
557 (line (nth 2 item))
558 (col (nth 3 item)))
559 (if (consp file) (setq file (car file)))
560 (if (consp line) (setq line (car line)))
561 (if (consp col) (setq col (car col)))
562
563 (when (not (functionp line))
564 (setq converted-list (cons (list regexp file line col) converted-list)))))
565 converted-list))
566
567(require 'compile)
568
569(defvar flymake-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text
570 (append
571 '(
572 ;; MS Visual C++ 6.0
573 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
574 1 3 nil 4)
575 ;; jikes
576 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)"
577 1 3 nil 4)
578 ;; MS midl
579 ("midl[ ]*:[ ]*\\(command line error .*\\)"
580 nil nil nil 1)
581 ;; MS C#
582 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
583 1 3 nil 4)
584 ;; perl
585 ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1)
586 ;; PHP
587 ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1)
588 ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1)
589 ;; ant/javac. Note this also matches gcc warnings!
590 (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)"
591 2 4 nil 5))
592 ;; compilation-error-regexp-alist)
593 (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist))
594 "Patterns for matching error/warning lines. Each pattern has the form
595\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX).
596Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns
597from compile.el")
598
599(define-obsolete-variable-alias 'flymake-warning-re 'flymake-warning-predicate "24.4")
600(defvar flymake-warning-predicate "^[wW]arning"
601 "Predicate matching against error text to detect a warning.
602Takes a single argument, the error's text and should return non-nil
603if it's a warning.
604Instead of a function, it can also be a regular expression.")
605
606(defun flymake-parse-line (line)
607 "Parse LINE to see if it is an error or warning.
608Return its components if so, nil otherwise."
609 (let ((raw-file-name nil)
610 (line-no 0)
611 (err-type "e")
612 (err-text nil)
613 (patterns flymake-err-line-patterns)
614 (matched nil))
615 (while (and patterns (not matched))
616 (when (string-match (car (car patterns)) line)
617 (let* ((file-idx (nth 1 (car patterns)))
618 (line-idx (nth 2 (car patterns))))
619
620 (setq raw-file-name (if file-idx (match-string file-idx line) nil))
621 (setq line-no (if line-idx (string-to-number
622 (match-string line-idx line)) 0))
623 (setq err-text (if (> (length (car patterns)) 4)
624 (match-string (nth 4 (car patterns)) line)
625 (flymake-patch-err-text
626 (substring line (match-end 0)))))
627 (if (null err-text)
628 (setq err-text "<no error text>")
629 (when (cond ((stringp flymake-warning-predicate)
630 (string-match flymake-warning-predicate err-text))
631 ((functionp flymake-warning-predicate)
632 (funcall flymake-warning-predicate err-text)))
633 (setq err-type "w")))
634 (flymake-log
635 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s"
636 file-idx line-idx raw-file-name line-no err-text)
637 (setq matched t)))
638 (setq patterns (cdr patterns)))
639 (if matched
640 (flymake-ler-make-ler raw-file-name line-no err-type err-text)
641 ())))
642
643(defun flymake-get-project-include-dirs-imp (basedir)
644 "Include dirs for the project current file belongs to."
645 (if (flymake-get-project-include-dirs-from-cache basedir)
646 (progn
647 (flymake-get-project-include-dirs-from-cache basedir))
648 ;;else
649 (let* ((command-line (concat "make -C "
650 (shell-quote-argument basedir)
651 " DUMPVARS=INCLUDE_DIRS dumpvars"))
652 (output (shell-command-to-string command-line))
653 (lines (split-string output "\n" t))
654 (count (length lines))
655 (idx 0)
656 (inc-dirs nil))
657 (while (and (< idx count) (not (string-match "^INCLUDE_DIRS=.*" (nth idx lines))))
658 (setq idx (1+ idx)))
659 (when (< idx count)
660 (let* ((inc-lines (split-string (nth idx lines) " *-I" t))
661 (inc-count (length inc-lines)))
662 (while (> inc-count 0)
663 (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines)))
664 (push (replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs))
665 (setq inc-count (1- inc-count)))))
666 (flymake-add-project-include-dirs-to-cache basedir inc-dirs)
667 inc-dirs)))
668
669(defvar flymake-get-project-include-dirs-function #'flymake-get-project-include-dirs-imp
670 "Function used to get project include dirs, one parameter: basedir name.")
671
672(defun flymake-get-project-include-dirs (basedir)
673 (funcall flymake-get-project-include-dirs-function basedir))
674
675(defun flymake-get-system-include-dirs ()
676 "System include dirs - from the `INCLUDE' env setting."
677 (let* ((includes (getenv "INCLUDE")))
678 (if includes (split-string includes path-separator t) nil)))
679
680(defvar flymake-project-include-dirs-cache (make-hash-table :test #'equal))
681
682(defun flymake-get-project-include-dirs-from-cache (base-dir)
683 (gethash base-dir flymake-project-include-dirs-cache))
684
685(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs)
686 (puthash base-dir include-dirs flymake-project-include-dirs-cache))
687
688(defun flymake-clear-project-include-dirs-cache ()
689 (clrhash flymake-project-include-dirs-cache))
690
691(defun flymake-get-include-dirs (base-dir)
692 "Get dirs to use when resolving local file names."
693 (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs))))
694 include-dirs))
695
696;; (defun flymake-restore-formatting ()
697;; "Remove any formatting made by flymake."
698;; )
699
700;; (defun flymake-get-program-dir (buffer)
701;; "Get dir to start program in."
702;; (unless (bufferp buffer)
703;; (error "Invalid buffer"))
704;; (with-current-buffer buffer
705;; default-directory))
706
707(defun flymake-safe-delete-file (file-name)
708 (when (and file-name (file-exists-p file-name))
709 (delete-file file-name)
710 (flymake-log 1 "deleted file %s" file-name)))
711
712(defun flymake-safe-delete-directory (dir-name)
713 (condition-case nil
714 (progn
715 (delete-directory dir-name)
716 (flymake-log 1 "deleted dir %s" dir-name))
717 (error
718 (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name))))
719
720(defun flymake-start-syntax-check ()
721 "Start syntax checking for current buffer."
722 (interactive)
723 (flymake-log 3 "flymake is running: %s" flymake-is-running)
724 (when (and (not flymake-is-running)
725 (flymake-can-syntax-check-file buffer-file-name))
726 (when (or (not flymake-compilation-prevents-syntax-check)
727 (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP")
728 (flymake-clear-buildfile-cache)
729 (flymake-clear-project-include-dirs-cache)
730
731 (setq flymake-check-was-interrupted nil)
732
733 (let* ((source-file-name buffer-file-name)
734 (init-f (flymake-get-init-function source-file-name))
735 (cleanup-f (flymake-get-cleanup-function source-file-name))
736 (cmd-and-args (funcall init-f))
737 (cmd (nth 0 cmd-and-args))
738 (args (nth 1 cmd-and-args))
739 (dir (nth 2 cmd-and-args)))
740 (if (not cmd-and-args)
741 (progn
742 (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name)
743 (funcall cleanup-f))
744 (progn
745 (setq flymake-last-change-time nil)
746 (flymake-start-syntax-check-process cmd args dir)))))))
747
748(defun flymake-start-syntax-check-process (cmd args dir)
749 "Start syntax check process."
750 (condition-case err
751 (let* ((process
752 (let ((default-directory (or dir default-directory)))
753 (when dir
754 (flymake-log 3 "starting process on dir %s" dir))
755 (apply 'start-file-process
756 "flymake-proc" (current-buffer) cmd args))))
757 (set-process-sentinel process 'flymake-process-sentinel)
758 (set-process-filter process 'flymake-process-filter)
759 (set-process-query-on-exit-flag process nil)
760 (push process flymake-processes)
761
762 (setq flymake-is-running t)
763 (setq flymake-last-change-time nil)
764 (setq flymake-check-start-time (float-time))
765
766 (flymake-report-status nil "*")
767 (flymake-log 2 "started process %d, command=%s, dir=%s"
768 (process-id process) (process-command process)
769 default-directory)
770 process)
771 (error
772 (let* ((err-str
773 (format-message
774 "Failed to launch syntax check process `%s' with args %s: %s"
775 cmd args (error-message-string err)))
776 (source-file-name buffer-file-name)
777 (cleanup-f (flymake-get-cleanup-function source-file-name)))
778 (flymake-log 0 err-str)
779 (funcall cleanup-f)
780 (flymake-report-fatal-status "PROCERR" err-str)))))
781
782(defun flymake-kill-process (proc)
783 "Kill process PROC."
784 (kill-process proc)
785 (let* ((buf (process-buffer proc)))
786 (when (buffer-live-p buf)
787 (with-current-buffer buf
788 (setq flymake-check-was-interrupted t))))
789 (flymake-log 1 "killed process %d" (process-id proc)))
790
791(defun flymake-stop-all-syntax-checks ()
792 "Kill all syntax check processes."
793 (interactive)
794 (while flymake-processes
795 (flymake-kill-process (pop flymake-processes))))
796
797(defun flymake-compilation-is-running ()
798 (and (boundp 'compilation-in-progress)
799 compilation-in-progress))
800
801(defun flymake-compile ()
802 "Kill all flymake syntax checks, start compilation."
803 (interactive)
804 (flymake-stop-all-syntax-checks)
805 (call-interactively 'compile))
806
807;;;; general init-cleanup and helper routines
808(defun flymake-create-temp-inplace (file-name prefix)
809 (unless (stringp file-name)
810 (error "Invalid file-name"))
811 (or prefix
812 (setq prefix "flymake"))
813 (let* ((ext (file-name-extension file-name))
814 (temp-name (file-truename
815 (concat (file-name-sans-extension file-name)
816 "_" prefix
817 (and ext (concat "." ext))))))
818 (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name)
819 temp-name))
820
821(defun flymake-create-temp-with-folder-structure (file-name _prefix)
822 (unless (stringp file-name)
823 (error "Invalid file-name"))
824
825 (let* ((dir (file-name-directory file-name))
826 ;; Not sure what this slash-pos is all about, but I guess it's just
827 ;; trying to remove the leading / of absolute file names.
828 (slash-pos (string-match "/" dir))
829 (temp-dir (expand-file-name (substring dir (1+ slash-pos))
830 temporary-file-directory)))
831
832 (file-truename (expand-file-name (file-name-nondirectory file-name)
833 temp-dir))))
834
835(defun flymake-delete-temp-directory (dir-name)
836 "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error."
837 (let* ((temp-dir temporary-file-directory)
838 (suffix (substring dir-name (1+ (length temp-dir)))))
839
840 (while (> (length suffix) 0)
841 (setq suffix (directory-file-name suffix))
842 ;;+(flymake-log 0 "suffix=%s" suffix)
843 (flymake-safe-delete-directory
844 (file-truename (expand-file-name suffix temp-dir)))
845 (setq suffix (file-name-directory suffix)))))
846
847(defvar-local flymake-temp-source-file-name nil)
848(defvar-local flymake-master-file-name nil)
849(defvar-local flymake-temp-master-file-name nil)
850(defvar-local flymake-base-dir nil)
851
852(defun flymake-init-create-temp-buffer-copy (create-temp-f)
853 "Make a temporary copy of the current buffer, save its name in buffer data and return the name."
854 (let* ((source-file-name buffer-file-name)
855 (temp-source-file-name (funcall create-temp-f source-file-name "flymake")))
856
857 (flymake-save-buffer-in-file temp-source-file-name)
858 (setq flymake-temp-source-file-name temp-source-file-name)
859 temp-source-file-name))
860
861(defun flymake-simple-cleanup ()
862 "Do cleanup after `flymake-init-create-temp-buffer-copy'.
863Delete temp file."
864 (flymake-safe-delete-file flymake-temp-source-file-name)
865 (setq flymake-last-change-time nil))
866
867(defun flymake-get-real-file-name (file-name-from-err-msg)
868 "Translate file name from error message to \"real\" file name.
869Return full-name. Names are real, not patched."
870 (let* ((real-name nil)
871 (source-file-name buffer-file-name)
872 (master-file-name flymake-master-file-name)
873 (temp-source-file-name flymake-temp-source-file-name)
874 (temp-master-file-name flymake-temp-master-file-name)
875 (base-dirs
876 (list flymake-base-dir
877 (file-name-directory source-file-name)
878 (if master-file-name (file-name-directory master-file-name))))
879 (files (list (list source-file-name source-file-name)
880 (list temp-source-file-name source-file-name)
881 (list master-file-name master-file-name)
882 (list temp-master-file-name master-file-name))))
883
884 (when (equal 0 (length file-name-from-err-msg))
885 (setq file-name-from-err-msg source-file-name))
886
887 (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files))
888 ;; if real-name is nil, than file name from err msg is none of the files we've patched
889 (if (not real-name)
890 (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs)))
891 (if (not real-name)
892 (setq real-name file-name-from-err-msg))
893 (setq real-name (flymake-fix-file-name real-name))
894 (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name)
895 real-name))
896
897(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files)
898 (let* ((base-dirs-count (length base-dirs))
899 (file-count (length files))
900 (real-name nil))
901
902 (while (and (not real-name) (> base-dirs-count 0))
903 (setq file-count (length files))
904 (while (and (not real-name) (> file-count 0))
905 (let* ((this-dir (nth (1- base-dirs-count) base-dirs))
906 (this-file (nth 0 (nth (1- file-count) files)))
907 (this-real-name (nth 1 (nth (1- file-count) files))))
908 ;;+(flymake-log 0 "this-dir=%s this-file=%s this-real=%s msg-file=%s" this-dir this-file this-real-name file-name-from-err-msg)
909 (when (and this-dir this-file (flymake-same-files
910 (expand-file-name file-name-from-err-msg this-dir)
911 this-file))
912 (setq real-name this-real-name)))
913 (setq file-count (1- file-count)))
914 (setq base-dirs-count (1- base-dirs-count)))
915 real-name))
916
917(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs)
918 (let* ((real-name nil))
919 (if (file-name-absolute-p file-name-from-err-msg)
920 (setq real-name file-name-from-err-msg)
921 (let* ((base-dirs-count (length base-dirs)))
922 (while (and (not real-name) (> base-dirs-count 0))
923 (let* ((full-name (expand-file-name file-name-from-err-msg
924 (nth (1- base-dirs-count) base-dirs))))
925 (if (file-exists-p full-name)
926 (setq real-name full-name))
927 (setq base-dirs-count (1- base-dirs-count))))))
928 real-name))
929
930(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name)
931 "Find buildfile, store its dir in buffer data and return its dir, if found."
932 (let* ((buildfile-dir
933 (flymake-find-buildfile buildfile-name
934 (file-name-directory source-file-name))))
935 (if buildfile-dir
936 (setq flymake-base-dir buildfile-dir)
937 (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name)
938 (flymake-report-fatal-status
939 "NOMK" (format "No buildfile (%s) found for %s"
940 buildfile-name source-file-name)))))
941
942(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp)
943 "Find master file (or buffer), create its copy along with a copy of the source file."
944 (let* ((source-file-name buffer-file-name)
945 (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))
946 (master-and-temp-master (flymake-create-master-file
947 source-file-name temp-source-file-name
948 get-incl-dirs-f create-temp-f
949 master-file-masks include-regexp)))
950
951 (if (not master-and-temp-master)
952 (progn
953 (flymake-log 1 "cannot find master file for %s" source-file-name)
954 (flymake-report-status "!" "") ; NOMASTER
955 nil)
956 (setq flymake-master-file-name (nth 0 master-and-temp-master))
957 (setq flymake-temp-master-file-name (nth 1 master-and-temp-master)))))
958
959(defun flymake-master-cleanup ()
960 (flymake-simple-cleanup)
961 (flymake-safe-delete-file flymake-temp-master-file-name))
962
963;;;; make-specific init-cleanup routines
964(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f)
965 "Create a command line for syntax check using GET-CMD-LINE-F."
966 (funcall get-cmd-line-f
967 (if use-relative-source
968 (file-relative-name source-file-name base-dir)
969 source-file-name)
970 (if use-relative-base-dir
971 (file-relative-name base-dir
972 (file-name-directory source-file-name))
973 base-dir)))
974
975(defun flymake-get-make-cmdline (source base-dir)
976 (list "make"
977 (list "-s"
978 "-C"
979 base-dir
980 (concat "CHK_SOURCES=" source)
981 "SYNTAX_CHECK_MODE=1"
982 "check-syntax")))
983
984(defun flymake-get-ant-cmdline (source base-dir)
985 (list "ant"
986 (list "-buildfile"
987 (concat base-dir "/" "build.xml")
988 (concat "-DCHK_SOURCES=" source)
989 "check-syntax")))
990
991(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f)
992 "Create syntax check command line for a directly checked source file.
993Use CREATE-TEMP-F for creating temp copy."
994 (let* ((args nil)
995 (source-file-name buffer-file-name)
996 (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name)))
997 (if buildfile-dir
998 (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)))
999 (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir
1000 use-relative-base-dir use-relative-source
1001 get-cmdline-f))))
1002 args))
1003
1004(defun flymake-simple-make-init ()
1005 (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline))
1006
1007(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp)
1008 "Create make command line for a source file checked via master file compilation."
1009 (let* ((make-args nil)
1010 (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
1011 get-incl-dirs-f 'flymake-create-temp-inplace
1012 master-file-masks include-regexp)))
1013 (when temp-master-file-name
1014 (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile")))
1015 (if buildfile-dir
1016 (setq make-args (flymake-get-syntax-check-program-args
1017 temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline)))))
1018 make-args))
1019
1020(defun flymake-find-make-buildfile (source-dir)
1021 (flymake-find-buildfile "Makefile" source-dir))
1022
1023;;;; .h/make specific
1024(defun flymake-master-make-header-init ()
1025 (flymake-master-make-init
1026 'flymake-get-include-dirs
1027 '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'")
1028 "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\""))
1029
1030;;;; .java/make specific
1031(defun flymake-simple-make-java-init ()
1032 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline))
1033
1034(defun flymake-simple-ant-java-init ()
1035 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline))
1036
1037(defun flymake-simple-java-cleanup ()
1038 "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs."
1039 (flymake-safe-delete-file flymake-temp-source-file-name)
1040 (when flymake-temp-source-file-name
1041 (flymake-delete-temp-directory
1042 (file-name-directory flymake-temp-source-file-name))))
1043
1044;;;; perl-specific init-cleanup routines
1045(defun flymake-perl-init ()
1046 (let* ((temp-file (flymake-init-create-temp-buffer-copy
1047 'flymake-create-temp-inplace))
1048 (local-file (file-relative-name
1049 temp-file
1050 (file-name-directory buffer-file-name))))
1051 (list "perl" (list "-wc " local-file))))
1052
1053;;;; php-specific init-cleanup routines
1054(defun flymake-php-init ()
1055 (let* ((temp-file (flymake-init-create-temp-buffer-copy
1056 'flymake-create-temp-inplace))
1057 (local-file (file-relative-name
1058 temp-file
1059 (file-name-directory buffer-file-name))))
1060 (list "php" (list "-f" local-file "-l"))))
1061
1062;;;; tex-specific init-cleanup routines
1063(defun flymake-get-tex-args (file-name)
1064 ;;(list "latex" (list "-c-style-errors" file-name))
1065 (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name)))
1066
1067(defun flymake-simple-tex-init ()
1068 (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace)))
1069
1070;; Perhaps there should be a buffer-local variable flymake-master-file
1071;; that people can set to override this stuff. Could inherit from
1072;; the similar AUCTeX variable.
1073(defun flymake-master-tex-init ()
1074 (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
1075 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace
1076 '("\\.tex\\'")
1077 "[ \t]*\\in\\(?:put\\|clude\\)[ \t]*{\\(.*%s\\)}")))
1078 (when temp-master-file-name
1079 (flymake-get-tex-args temp-master-file-name))))
1080
1081(defun flymake-get-include-dirs-dot (_base-dir)
1082 '("."))
1083
1084;;;; xml-specific init-cleanup routines
1085(defun flymake-xml-init ()
1086 (list flymake-xml-program
1087 (list "val" (flymake-init-create-temp-buffer-copy
1088 'flymake-create-temp-inplace))))
1089
1090(provide 'flymake-proc)
1091;;; flymake-proc.el ends here
diff --git a/lisp/progmodes/flymake-ui.el b/lisp/progmodes/flymake-ui.el
deleted file mode 100644
index 2a15a497d84..00000000000
--- a/lisp/progmodes/flymake-ui.el
+++ /dev/null
@@ -1,604 +0,0 @@
1;;; flymake-ui.el --- A universal on-the-fly syntax checker -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2003-2017 Free Software Foundation, Inc.
4
5;; Author: Pavel Kobyakov <pk_at_work@yahoo.com>
6;; Maintainer: Leo Liu <sdl.web@gmail.com>
7;; Version: 0.3
8;; Keywords: c languages tools
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
24
25;;; Commentary:
26;;
27;; Flymake is a minor Emacs mode performing on-the-fly syntax checks.xo
28;;
29;; This file contains the UI for displaying and interacting with the
30;; results of such checks, as well as entry points for backends to
31;; hook on to. Backends are sources of diagnostic info.
32;;
33;;; Code:
34
35(eval-when-compile (require 'cl-lib))
36
37(defgroup flymake nil
38 "Universal on-the-fly syntax checker."
39 :version "23.1"
40 :link '(custom-manual "(flymake) Top")
41 :group 'tools)
42
43(defcustom flymake-error-bitmap '(exclamation-mark error)
44 "Bitmap (a symbol) used in the fringe for indicating errors.
45The value may also be a list of two elements where the second
46element specifies the face for the bitmap. For possible bitmap
47symbols, see `fringe-bitmaps'. See also `flymake-warning-bitmap'.
48
49The option `flymake-fringe-indicator-position' controls how and where
50this is used."
51 :group 'flymake
52 :version "24.3"
53 :type '(choice (symbol :tag "Bitmap")
54 (list :tag "Bitmap and face"
55 (symbol :tag "Bitmap")
56 (face :tag "Face"))))
57
58(defcustom flymake-warning-bitmap 'question-mark
59 "Bitmap (a symbol) used in the fringe for indicating warnings.
60The value may also be a list of two elements where the second
61element specifies the face for the bitmap. For possible bitmap
62symbols, see `fringe-bitmaps'. See also `flymake-error-bitmap'.
63
64The option `flymake-fringe-indicator-position' controls how and where
65this is used."
66 :group 'flymake
67 :version "24.3"
68 :type '(choice (symbol :tag "Bitmap")
69 (list :tag "Bitmap and face"
70 (symbol :tag "Bitmap")
71 (face :tag "Face"))))
72
73(defcustom flymake-fringe-indicator-position 'left-fringe
74 "The position to put flymake fringe indicator.
75The value can be nil (do not use indicators), `left-fringe' or `right-fringe'.
76See `flymake-error-bitmap' and `flymake-warning-bitmap'."
77 :group 'flymake
78 :version "24.3"
79 :type '(choice (const left-fringe)
80 (const right-fringe)
81 (const :tag "No fringe indicators" nil)))
82
83(defcustom flymake-start-syntax-check-on-newline t
84 "Start syntax check if newline char was added/removed from the buffer."
85 :group 'flymake
86 :type 'boolean)
87
88(defcustom flymake-no-changes-timeout 0.5
89 "Time to wait after last change before starting compilation."
90 :group 'flymake
91 :type 'number)
92
93(defcustom flymake-gui-warnings-enabled t
94 "Enables/disables GUI warnings."
95 :group 'flymake
96 :type 'boolean)
97(make-obsolete-variable 'flymake-gui-warnings-enabled
98 "it no longer has any effect." "26.1")
99
100(defcustom flymake-start-syntax-check-on-find-file t
101 "Start syntax check on find file."
102 :group 'flymake
103 :type 'boolean)
104
105(defcustom flymake-log-level -1
106 "Logging level, only messages with level lower or equal will be logged.
107-1 = NONE, 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG"
108 :group 'flymake
109 :type 'integer)
110
111(defvar-local flymake-timer nil
112 "Timer for starting syntax check.")
113
114(defvar-local flymake-last-change-time nil
115 "Time of last buffer change.")
116
117(defvar-local flymake-check-start-time nil
118 "Time at which syntax check was started.")
119
120(defvar-local flymake-check-was-interrupted nil
121 "Non-nil if syntax check was killed by `flymake-compile'.")
122
123(defvar-local flymake-err-info nil
124 "Sorted list of line numbers and lists of err info in the form (file, err-text).")
125
126(defvar-local flymake-new-err-info nil
127 "Same as `flymake-err-info', effective when a syntax check is in progress.")
128
129(defun flymake-log (level text &rest args)
130 "Log a message at level LEVEL.
131If LEVEL is higher than `flymake-log-level', the message is
132ignored. Otherwise, it is printed using `message'.
133TEXT is a format control string, and the remaining arguments ARGS
134are the string substitutions (see the function `format')."
135 (if (<= level flymake-log-level)
136 (let* ((msg (apply #'format-message text args)))
137 (message "%s" msg))))
138
139(defun flymake-ins-after (list pos val)
140 "Insert VAL into LIST after position POS.
141POS counts from zero."
142 (let ((tmp (copy-sequence list)))
143 (setcdr (nthcdr pos tmp) (cons val (nthcdr (1+ pos) tmp)))
144 tmp))
145
146(defun flymake-set-at (list pos val)
147 "Set VAL at position POS in LIST.
148POS counts from zero."
149 (let ((tmp (copy-sequence list)))
150 (setcar (nthcdr pos tmp) val)
151 tmp))
152
153(defun flymake-er-make-er (line-no line-err-info-list)
154 (list line-no line-err-info-list))
155
156(defun flymake-er-get-line (err-info)
157 (nth 0 err-info))
158
159(defun flymake-er-get-line-err-info-list (err-info)
160 (nth 1 err-info))
161
162(cl-defstruct (flymake-ler
163 (:constructor nil)
164 (:constructor flymake-ler-make-ler (file line type text &optional full-file)))
165 file line type text full-file)
166
167(defun flymake-ler-set-file (line-err-info file)
168 (flymake-ler-make-ler file
169 (flymake-ler-line line-err-info)
170 (flymake-ler-type line-err-info)
171 (flymake-ler-text line-err-info)
172 (flymake-ler-full-file line-err-info)))
173
174(defun flymake-ler-set-full-file (line-err-info full-file)
175 (flymake-ler-make-ler (flymake-ler-file line-err-info)
176 (flymake-ler-line line-err-info)
177 (flymake-ler-type line-err-info)
178 (flymake-ler-text line-err-info)
179 full-file))
180
181(defun flymake-ler-set-line (line-err-info line)
182 (flymake-ler-make-ler (flymake-ler-file line-err-info)
183 line
184 (flymake-ler-type line-err-info)
185 (flymake-ler-text line-err-info)
186 (flymake-ler-full-file line-err-info)))
187
188(defun flymake-get-line-err-count (line-err-info-list type)
189 "Return number of errors of specified TYPE.
190Value of TYPE is either \"e\" or \"w\"."
191 (let* ((idx 0)
192 (count (length line-err-info-list))
193 (err-count 0))
194
195 (while (< idx count)
196 (when (equal type (flymake-ler-type (nth idx line-err-info-list)))
197 (setq err-count (1+ err-count)))
198 (setq idx (1+ idx)))
199 err-count))
200
201(defun flymake-get-err-count (err-info-list type)
202 "Return number of errors of specified TYPE for ERR-INFO-LIST."
203 (let* ((idx 0)
204 (count (length err-info-list))
205 (err-count 0))
206 (while (< idx count)
207 (setq err-count (+ err-count (flymake-get-line-err-count (nth 1 (nth idx err-info-list)) type)))
208 (setq idx (1+ idx)))
209 err-count))
210
211(defun flymake-highlight-err-lines (err-info-list)
212 "Highlight error lines in BUFFER using info from ERR-INFO-LIST."
213 (save-excursion
214 (dolist (err err-info-list)
215 (flymake-highlight-line (car err) (nth 1 err)))))
216
217(defun flymake-overlay-p (ov)
218 "Determine whether overlay OV was created by flymake."
219 (and (overlayp ov) (overlay-get ov 'flymake-overlay)))
220
221(defun flymake-make-overlay (beg end tooltip-text face bitmap)
222 "Allocate a flymake overlay in range BEG and END."
223 (when (not (flymake-region-has-flymake-overlays beg end))
224 (let ((ov (make-overlay beg end nil t))
225 (fringe (and flymake-fringe-indicator-position
226 (propertize "!" 'display
227 (cons flymake-fringe-indicator-position
228 (if (listp bitmap)
229 bitmap
230 (list bitmap)))))))
231 (overlay-put ov 'face face)
232 (overlay-put ov 'help-echo tooltip-text)
233 (overlay-put ov 'flymake-overlay t)
234 (overlay-put ov 'priority 100)
235 (overlay-put ov 'evaporate t)
236 (overlay-put ov 'before-string fringe)
237 ;;+(flymake-log 3 "created overlay %s" ov)
238 ov)
239 (flymake-log 3 "created an overlay at (%d-%d)" beg end)))
240
241(defun flymake-delete-own-overlays ()
242 "Delete all flymake overlays in BUFFER."
243 (dolist (ol (overlays-in (point-min) (point-max)))
244 (when (flymake-overlay-p ol)
245 (delete-overlay ol)
246 ;;+(flymake-log 3 "deleted overlay %s" ol)
247 )))
248
249(defun flymake-region-has-flymake-overlays (beg end)
250 "Check if region specified by BEG and END has overlay.
251Return t if it has at least one flymake overlay, nil if no overlay."
252 (let ((ov (overlays-in beg end))
253 (has-flymake-overlays nil))
254 (while (consp ov)
255 (when (flymake-overlay-p (car ov))
256 (setq has-flymake-overlays t))
257 (setq ov (cdr ov)))
258 has-flymake-overlays))
259
260(defface flymake-errline
261 '((((supports :underline (:style wave)))
262 :underline (:style wave :color "Red1"))
263 (t
264 :inherit error))
265 "Face used for marking error lines."
266 :version "24.4"
267 :group 'flymake)
268
269(defface flymake-warnline
270 '((((supports :underline (:style wave)))
271 :underline (:style wave :color "DarkOrange"))
272 (t
273 :inherit warning))
274 "Face used for marking warning lines."
275 :version "24.4"
276 :group 'flymake)
277
278(defun flymake-highlight-line (line-no line-err-info-list)
279 "Highlight line LINE-NO in current buffer.
280Perhaps use text from LINE-ERR-INFO-LIST to enhance highlighting."
281 (goto-char (point-min))
282 (forward-line (1- line-no))
283 (pcase-let* ((beg (progn (back-to-indentation) (point)))
284 (end (progn
285 (end-of-line)
286 (skip-chars-backward " \t\f\t\n" beg)
287 (if (eq (point) beg)
288 (line-beginning-position 2)
289 (point))))
290 (tooltip-text (mapconcat #'flymake-ler-text line-err-info-list "\n"))
291 (`(,face ,bitmap)
292 (if (> (flymake-get-line-err-count line-err-info-list "e") 0)
293 (list 'flymake-errline flymake-error-bitmap)
294 (list 'flymake-warnline flymake-warning-bitmap))))
295 (flymake-make-overlay beg end tooltip-text face bitmap)))
296
297(defun flymake-find-err-info (err-info-list line-no)
298 "Find (line-err-info-list pos) for specified LINE-NO."
299 (if err-info-list
300 (let* ((line-err-info-list nil)
301 (pos 0)
302 (count (length err-info-list)))
303
304 (while (and (< pos count) (< (car (nth pos err-info-list)) line-no))
305 (setq pos (1+ pos)))
306 (when (and (< pos count) (equal (car (nth pos err-info-list)) line-no))
307 (setq line-err-info-list (flymake-er-get-line-err-info-list (nth pos err-info-list))))
308 (list line-err-info-list pos))
309 '(nil 0)))
310
311(defun flymake-line-err-info-is-less-or-equal (line-one line-two)
312 (or (string< (flymake-ler-type line-one) (flymake-ler-type line-two))
313 (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two))
314 (not (flymake-ler-file line-one)) (flymake-ler-file line-two))
315 (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two))
316 (or (and (flymake-ler-file line-one) (flymake-ler-file line-two))
317 (and (not (flymake-ler-file line-one)) (not (flymake-ler-file line-two)))))))
318
319(defun flymake-add-line-err-info (line-err-info-list line-err-info)
320 "Update LINE-ERR-INFO-LIST with the error LINE-ERR-INFO.
321For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'.
322The new element is inserted in the proper position, according to
323the predicate `flymake-line-err-info-is-less-or-equal'.
324The updated value of LINE-ERR-INFO-LIST is returned."
325 (if (not line-err-info-list)
326 (list line-err-info)
327 (let* ((count (length line-err-info-list))
328 (idx 0))
329 (while (and (< idx count) (flymake-line-err-info-is-less-or-equal (nth idx line-err-info-list) line-err-info))
330 (setq idx (1+ idx)))
331 (cond ((equal 0 idx) (setq line-err-info-list (cons line-err-info line-err-info-list)))
332 (t (setq line-err-info-list (flymake-ins-after line-err-info-list (1- idx) line-err-info))))
333 line-err-info-list)))
334
335(defun flymake-add-err-info (err-info-list line-err-info)
336 "Update ERR-INFO-LIST with the error LINE-ERR-INFO, preserving sort order.
337Returns the updated value of ERR-INFO-LIST.
338For the format of ERR-INFO-LIST, see `flymake-err-info'.
339For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'."
340 (let* ((line-no (if (flymake-ler-file line-err-info) 1 (flymake-ler-line line-err-info)))
341 (info-and-pos (flymake-find-err-info err-info-list line-no))
342 (exists (car info-and-pos))
343 (pos (nth 1 info-and-pos))
344 (line-err-info-list nil)
345 (err-info nil))
346
347 (if exists
348 (setq line-err-info-list (flymake-er-get-line-err-info-list (car (nthcdr pos err-info-list)))))
349 (setq line-err-info-list (flymake-add-line-err-info line-err-info-list line-err-info))
350
351 (setq err-info (flymake-er-make-er line-no line-err-info-list))
352 (cond (exists (setq err-info-list (flymake-set-at err-info-list pos err-info)))
353 ((equal 0 pos) (setq err-info-list (cons err-info err-info-list)))
354 (t (setq err-info-list (flymake-ins-after err-info-list (1- pos) err-info))))
355 err-info-list))
356
357(defvar-local flymake-is-running nil
358 "If t, flymake syntax check process is running for the current buffer.")
359
360(defun flymake-on-timer-event (buffer)
361 "Start a syntax check for buffer BUFFER if necessary."
362 (when (buffer-live-p buffer)
363 (with-current-buffer buffer
364 (when (and (not flymake-is-running)
365 flymake-last-change-time
366 (> (- (float-time) flymake-last-change-time)
367 flymake-no-changes-timeout))
368
369 (setq flymake-last-change-time nil)
370 (flymake-log 3 "starting syntax check as more than 1 second passed since last change")
371 (flymake-start-syntax-check)))))
372
373(define-obsolete-function-alias 'flymake-display-err-menu-for-current-line
374 'flymake-popup-current-error-menu "24.4")
375
376(defun flymake-popup-current-error-menu (&optional event)
377 "Pop up a menu with errors/warnings for current line."
378 (interactive (list last-nonmenu-event))
379 (let* ((line-no (line-number-at-pos))
380 (errors (or (car (flymake-find-err-info flymake-err-info line-no))
381 (user-error "No errors for current line")))
382 (menu (mapcar (lambda (x)
383 (if (flymake-ler-file x)
384 (cons (format "%s - %s(%d)"
385 (flymake-ler-text x)
386 (flymake-ler-file x)
387 (flymake-ler-line x))
388 x)
389 (list (flymake-ler-text x))))
390 errors))
391 (event (if (mouse-event-p event)
392 event
393 (list 'mouse-1 (posn-at-point))))
394 (title (format "Line %d: %d error(s), %d warning(s)"
395 line-no
396 (flymake-get-line-err-count errors "e")
397 (flymake-get-line-err-count errors "w")))
398 (choice (x-popup-menu event (list title (cons "" menu)))))
399 (flymake-log 3 "choice=%s" choice)
400 (when choice
401 (flymake-goto-file-and-line (flymake-ler-full-file choice)
402 (flymake-ler-line choice)))))
403
404(defun flymake-goto-file-and-line (file line)
405 "Try to get buffer for FILE and goto line LINE in it."
406 (if (not (file-exists-p file))
407 (flymake-log 1 "File %s does not exist" file)
408 (find-file file)
409 (goto-char (point-min))
410 (forward-line (1- line))))
411
412;; flymake minor mode declarations
413(defvar-local flymake-mode-line nil)
414(defvar-local flymake-mode-line-e-w nil)
415(defvar-local flymake-mode-line-status nil)
416
417(defun flymake-report-status (e-w &optional status)
418 "Show status in mode line."
419 (when e-w
420 (setq flymake-mode-line-e-w e-w))
421 (when status
422 (setq flymake-mode-line-status status))
423 (let* ((mode-line " Flymake"))
424 (when (> (length flymake-mode-line-e-w) 0)
425 (setq mode-line (concat mode-line ":" flymake-mode-line-e-w)))
426 (setq mode-line (concat mode-line flymake-mode-line-status))
427 (setq flymake-mode-line mode-line)
428 (force-mode-line-update)))
429
430;; Nothing in flymake uses this at all any more, so this is just for
431;; third-party compatibility.
432(define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1")
433
434(defun flymake-report-fatal-status (status warning)
435 "Display a warning and switch flymake mode off."
436 ;; This first message was always shown by default, and flymake-log
437 ;; does nothing by default, hence the use of message.
438 ;; Another option is display-warning.
439 (if (< flymake-log-level 0)
440 (message "Flymake: %s. Flymake will be switched OFF" warning))
441 (flymake-mode 0)
442 (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s"
443 (buffer-name) status warning))
444
445;;;###autoload
446(define-minor-mode flymake-mode nil
447 :group 'flymake :lighter flymake-mode-line
448 (cond
449
450 ;; Turning the mode ON.
451 (flymake-mode
452 (cond
453 ((not buffer-file-name)
454 (message "Flymake unable to run without a buffer file name"))
455 ((not (flymake-can-syntax-check-file buffer-file-name))
456 (flymake-log 2 "flymake cannot check syntax in buffer %s" (buffer-name)))
457 (t
458 (add-hook 'after-change-functions 'flymake-after-change-function nil t)
459 (add-hook 'after-save-hook 'flymake-after-save-hook nil t)
460 (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t)
461 ;;+(add-hook 'find-file-hook 'flymake-find-file-hook)
462
463 (flymake-report-status "" "")
464
465 (setq flymake-timer
466 (run-at-time nil 1 'flymake-on-timer-event (current-buffer)))
467
468 (when (and flymake-start-syntax-check-on-find-file
469 ;; Since we write temp files in current dir, there's no point
470 ;; trying if the directory is read-only (bug#8954).
471 (file-writable-p (file-name-directory buffer-file-name)))
472 (with-demoted-errors
473 (flymake-start-syntax-check))))))
474
475 ;; Turning the mode OFF.
476 (t
477 (remove-hook 'after-change-functions 'flymake-after-change-function t)
478 (remove-hook 'after-save-hook 'flymake-after-save-hook t)
479 (remove-hook 'kill-buffer-hook 'flymake-kill-buffer-hook t)
480 ;;+(remove-hook 'find-file-hook (function flymake-find-file-hook) t)
481
482 (flymake-delete-own-overlays)
483
484 (when flymake-timer
485 (cancel-timer flymake-timer)
486 (setq flymake-timer nil))
487
488 (setq flymake-is-running nil))))
489
490;; disabling flymake-mode is safe, enabling - not necessarily so
491(put 'flymake-mode 'safe-local-variable 'null)
492
493;;;###autoload
494(defun flymake-mode-on ()
495 "Turn flymake mode on."
496 (flymake-mode 1)
497 (flymake-log 1 "flymake mode turned ON for buffer %s" (buffer-name)))
498
499;;;###autoload
500(defun flymake-mode-off ()
501 "Turn flymake mode off."
502 (flymake-mode 0)
503 (flymake-log 1 "flymake mode turned OFF for buffer %s" (buffer-name)))
504
505(defun flymake-after-change-function (start stop _len)
506 "Start syntax check for current buffer if it isn't already running."
507 ;;+(flymake-log 0 "setting change time to %s" (float-time))
508 (let((new-text (buffer-substring start stop)))
509 (when (and flymake-start-syntax-check-on-newline (equal new-text "\n"))
510 (flymake-log 3 "starting syntax check as new-line has been seen")
511 (flymake-start-syntax-check))
512 (setq flymake-last-change-time (float-time))))
513
514(defun flymake-after-save-hook ()
515 (if (local-variable-p 'flymake-mode (current-buffer)) ; (???) other way to determine whether flymake is active in buffer being saved?
516 (progn
517 (flymake-log 3 "starting syntax check as buffer was saved")
518 (flymake-start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???)
519
520(defun flymake-kill-buffer-hook ()
521 (when flymake-timer
522 (cancel-timer flymake-timer)
523 (setq flymake-timer nil)))
524
525;;;###autoload
526(defun flymake-find-file-hook ()
527 ;;+(when flymake-start-syntax-check-on-find-file
528 ;;+ (flymake-log 3 "starting syntax check on file open")
529 ;;+ (flymake-start-syntax-check)
530 ;;+)
531 (when (and (not (local-variable-p 'flymake-mode (current-buffer)))
532 (flymake-can-syntax-check-file buffer-file-name))
533 (flymake-mode)
534 (flymake-log 3 "automatically turned ON flymake mode")))
535
536(defun flymake-get-first-err-line-no (err-info-list)
537 "Return first line with error."
538 (when err-info-list
539 (flymake-er-get-line (car err-info-list))))
540
541(defun flymake-get-last-err-line-no (err-info-list)
542 "Return last line with error."
543 (when err-info-list
544 (flymake-er-get-line (nth (1- (length err-info-list)) err-info-list))))
545
546(defun flymake-get-next-err-line-no (err-info-list line-no)
547 "Return next line with error."
548 (when err-info-list
549 (let* ((count (length err-info-list))
550 (idx 0))
551 (while (and (< idx count) (>= line-no (flymake-er-get-line (nth idx err-info-list))))
552 (setq idx (1+ idx)))
553 (if (< idx count)
554 (flymake-er-get-line (nth idx err-info-list))))))
555
556(defun flymake-get-prev-err-line-no (err-info-list line-no)
557 "Return previous line with error."
558 (when err-info-list
559 (let* ((count (length err-info-list)))
560 (while (and (> count 0) (<= line-no (flymake-er-get-line (nth (1- count) err-info-list))))
561 (setq count (1- count)))
562 (if (> count 0)
563 (flymake-er-get-line (nth (1- count) err-info-list))))))
564
565(defun flymake-skip-whitespace ()
566 "Move forward until non-whitespace is reached."
567 (while (looking-at "[ \t]")
568 (forward-char)))
569
570(defun flymake-goto-line (line-no)
571 "Go to line LINE-NO, then skip whitespace."
572 (goto-char (point-min))
573 (forward-line (1- line-no))
574 (flymake-skip-whitespace))
575
576(defun flymake-goto-next-error ()
577 "Go to next error in err ring."
578 (interactive)
579 (let ((line-no (flymake-get-next-err-line-no flymake-err-info (line-number-at-pos))))
580 (when (not line-no)
581 (setq line-no (flymake-get-first-err-line-no flymake-err-info))
582 (flymake-log 1 "passed end of file"))
583 (if line-no
584 (flymake-goto-line line-no)
585 (flymake-log 1 "no errors in current buffer"))))
586
587(defun flymake-goto-prev-error ()
588 "Go to previous error in err ring."
589 (interactive)
590 (let ((line-no (flymake-get-prev-err-line-no flymake-err-info (line-number-at-pos))))
591 (when (not line-no)
592 (setq line-no (flymake-get-last-err-line-no flymake-err-info))
593 (flymake-log 1 "passed beginning of file"))
594 (if line-no
595 (flymake-goto-line line-no)
596 (flymake-log 1 "no errors in current buffer"))))
597
598(defun flymake-patch-err-text (string)
599 (if (string-match "^[\n\t :0-9]*\\(.*\\)$" string)
600 (match-string 1 string)
601 string))
602
603(provide 'flymake-ui)
604;;; flymake-ui.el ends here
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index 059bce95eed..866116fbecd 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -24,18 +24,1629 @@
24 24
25;;; Commentary: 25;;; Commentary:
26;; 26;;
27;; Flymake is a minor Emacs mode performing on-the-fly syntax checks. 27;; Flymake is a minor Emacs mode performing on-the-fly syntax checks
28;; 28;; using the external syntax check tool (for C/C++ this is usually the
29;; It collects diagnostic information for multiple sources and 29;; compiler).
30;; visually annotates the relevant lines in the buffer. 30
31;; 31;;; Bugs/todo:
32;; This file is just a stub for that loads the UI and backends, which 32
33;; could also be loaded separately. 33;; - Only uses "Makefile", not "makefile" or "GNUmakefile"
34;; (from http://bugs.debian.org/337339).
34 35
35;;; Code: 36;;; Code:
36 37
37(require 'flymake-ui) 38(eval-when-compile (require 'cl-lib))
38(require 'flymake-proc) 39
40(defgroup flymake nil
41 "Universal on-the-fly syntax checker."
42 :version "23.1"
43 :link '(custom-manual "(flymake) Top")
44 :group 'tools)
45
46(defcustom flymake-error-bitmap '(exclamation-mark error)
47 "Bitmap (a symbol) used in the fringe for indicating errors.
48The value may also be a list of two elements where the second
49element specifies the face for the bitmap. For possible bitmap
50symbols, see `fringe-bitmaps'. See also `flymake-warning-bitmap'.
51
52The option `flymake-fringe-indicator-position' controls how and where
53this is used."
54 :group 'flymake
55 :version "24.3"
56 :type '(choice (symbol :tag "Bitmap")
57 (list :tag "Bitmap and face"
58 (symbol :tag "Bitmap")
59 (face :tag "Face"))))
60
61(defcustom flymake-warning-bitmap 'question-mark
62 "Bitmap (a symbol) used in the fringe for indicating warnings.
63The value may also be a list of two elements where the second
64element specifies the face for the bitmap. For possible bitmap
65symbols, see `fringe-bitmaps'. See also `flymake-error-bitmap'.
66
67The option `flymake-fringe-indicator-position' controls how and where
68this is used."
69 :group 'flymake
70 :version "24.3"
71 :type '(choice (symbol :tag "Bitmap")
72 (list :tag "Bitmap and face"
73 (symbol :tag "Bitmap")
74 (face :tag "Face"))))
75
76(defcustom flymake-fringe-indicator-position 'left-fringe
77 "The position to put flymake fringe indicator.
78The value can be nil (do not use indicators), `left-fringe' or `right-fringe'.
79See `flymake-error-bitmap' and `flymake-warning-bitmap'."
80 :group 'flymake
81 :version "24.3"
82 :type '(choice (const left-fringe)
83 (const right-fringe)
84 (const :tag "No fringe indicators" nil)))
85
86(defcustom flymake-compilation-prevents-syntax-check t
87 "If non-nil, don't start syntax check if compilation is running."
88 :group 'flymake
89 :type 'boolean)
90
91(defcustom flymake-start-syntax-check-on-newline t
92 "Start syntax check if newline char was added/removed from the buffer."
93 :group 'flymake
94 :type 'boolean)
95
96(defcustom flymake-no-changes-timeout 0.5
97 "Time to wait after last change before starting compilation."
98 :group 'flymake
99 :type 'number)
100
101(defcustom flymake-gui-warnings-enabled t
102 "Enables/disables GUI warnings."
103 :group 'flymake
104 :type 'boolean)
105(make-obsolete-variable 'flymake-gui-warnings-enabled
106 "it no longer has any effect." "26.1")
107
108(defcustom flymake-start-syntax-check-on-find-file t
109 "Start syntax check on find file."
110 :group 'flymake
111 :type 'boolean)
112
113(defcustom flymake-log-level -1
114 "Logging level, only messages with level lower or equal will be logged.
115-1 = NONE, 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG"
116 :group 'flymake
117 :type 'integer)
118
119(defcustom flymake-xml-program
120 (if (executable-find "xmlstarlet") "xmlstarlet" "xml")
121 "Program to use for XML validation."
122 :type 'file
123 :group 'flymake
124 :version "24.4")
125
126(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest")
127 "Dirs where to look for master files."
128 :group 'flymake
129 :type '(repeat (string)))
130
131(defcustom flymake-master-file-count-limit 32
132 "Max number of master files to check."
133 :group 'flymake
134 :type 'integer)
135
136(defcustom flymake-allowed-file-name-masks
137 '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-simple-make-init)
138 ("\\.xml\\'" flymake-xml-init)
139 ("\\.html?\\'" flymake-xml-init)
140 ("\\.cs\\'" flymake-simple-make-init)
141 ("\\.p[ml]\\'" flymake-perl-init)
142 ("\\.php[345]?\\'" flymake-php-init)
143 ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup)
144 ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup)
145 ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup)
146 ("\\.tex\\'" flymake-simple-tex-init)
147 ("\\.idl\\'" flymake-simple-make-init)
148 ;; ("\\.cpp\\'" 1)
149 ;; ("\\.java\\'" 3)
150 ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'")
151 ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2))
152 ;; ("\\.idl\\'" 1)
153 ;; ("\\.odl\\'" 1)
154 ;; ("[0-9]+\\.tex\\'" 2 ("\\.tex\\'")
155 ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 ))
156 ;; ("\\.tex\\'" 1)
157 )
158 "Files syntax checking is allowed for.
159This is an alist with elements of the form:
160 REGEXP INIT [CLEANUP [NAME]]
161REGEXP is a regular expression that matches a file name.
162INIT is the init function to use, missing means disable `flymake-mode'.
163CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'.
164NAME is the file name function to use, default `flymake-get-real-file-name'."
165 :group 'flymake
166 :type '(alist :key-type (regexp :tag "File regexp")
167 :value-type
168 (list :tag "Handler functions"
169 (choice :tag "Init function"
170 (const :tag "disable" nil)
171 function)
172 (choice :tag "Cleanup function"
173 (const :tag "flymake-simple-cleanup" nil)
174 function)
175 (choice :tag "Name function"
176 (const :tag "flymake-get-real-file-name" nil)
177 function))))
178
179(defvar-local flymake-is-running nil
180 "If t, flymake syntax check process is running for the current buffer.")
181
182(defvar-local flymake-timer nil
183 "Timer for starting syntax check.")
184
185(defvar-local flymake-last-change-time nil
186 "Time of last buffer change.")
187
188(defvar-local flymake-check-start-time nil
189 "Time at which syntax check was started.")
190
191(defvar-local flymake-check-was-interrupted nil
192 "Non-nil if syntax check was killed by `flymake-compile'.")
193
194(defvar-local flymake-err-info nil
195 "Sorted list of line numbers and lists of err info in the form (file, err-text).")
196
197(defvar-local flymake-new-err-info nil
198 "Same as `flymake-err-info', effective when a syntax check is in progress.")
199
200(defun flymake-log (level text &rest args)
201 "Log a message at level LEVEL.
202If LEVEL is higher than `flymake-log-level', the message is
203ignored. Otherwise, it is printed using `message'.
204TEXT is a format control string, and the remaining arguments ARGS
205are the string substitutions (see the function `format')."
206 (if (<= level flymake-log-level)
207 (let* ((msg (apply #'format-message text args)))
208 (message "%s" msg))))
209
210(defun flymake-ins-after (list pos val)
211 "Insert VAL into LIST after position POS.
212POS counts from zero."
213 (let ((tmp (copy-sequence list)))
214 (setcdr (nthcdr pos tmp) (cons val (nthcdr (1+ pos) tmp)))
215 tmp))
216
217(defun flymake-set-at (list pos val)
218 "Set VAL at position POS in LIST.
219POS counts from zero."
220 (let ((tmp (copy-sequence list)))
221 (setcar (nthcdr pos tmp) val)
222 tmp))
223
224(defvar flymake-processes nil
225 "List of currently active flymake processes.")
226
227(defvar-local flymake-output-residual nil)
228
229(defun flymake-get-file-name-mode-and-masks (file-name)
230 "Return the corresponding entry from `flymake-allowed-file-name-masks'."
231 (unless (stringp file-name)
232 (error "Invalid file-name"))
233 (let ((fnm flymake-allowed-file-name-masks)
234 (mode-and-masks nil))
235 (while (and (not mode-and-masks) fnm)
236 (let ((item (pop fnm)))
237 (when (string-match (car item) file-name)
238 (setq mode-and-masks item)))) ; (cdr item) may be nil
239 (setq mode-and-masks (cdr mode-and-masks))
240 (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks))
241 mode-and-masks))
242
243(defun flymake-can-syntax-check-file (file-name)
244 "Determine whether we can syntax check FILE-NAME.
245Return nil if we cannot, non-nil if we can."
246 (if (flymake-get-init-function file-name) t nil))
247
248(defun flymake-get-init-function (file-name)
249 "Return init function to be used for the file."
250 (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name))))
251 ;;(flymake-log 0 "calling %s" init-f)
252 ;;(funcall init-f (current-buffer))
253 init-f))
254
255(defun flymake-get-cleanup-function (file-name)
256 "Return cleanup function to be used for the file."
257 (or (nth 1 (flymake-get-file-name-mode-and-masks file-name))
258 'flymake-simple-cleanup))
259
260(defun flymake-get-real-file-name-function (file-name)
261 (or (nth 2 (flymake-get-file-name-mode-and-masks file-name))
262 'flymake-get-real-file-name))
263
264(defvar flymake-find-buildfile-cache (make-hash-table :test #'equal))
265
266(defun flymake-get-buildfile-from-cache (dir-name)
267 "Look up DIR-NAME in cache and return its associated value.
268If DIR-NAME is not found, return nil."
269 (gethash dir-name flymake-find-buildfile-cache))
270
271(defun flymake-add-buildfile-to-cache (dir-name buildfile)
272 "Associate DIR-NAME with BUILDFILE in the buildfile cache."
273 (puthash dir-name buildfile flymake-find-buildfile-cache))
274
275(defun flymake-clear-buildfile-cache ()
276 "Clear the buildfile cache."
277 (clrhash flymake-find-buildfile-cache))
278
279(defun flymake-find-buildfile (buildfile-name source-dir-name)
280 "Find buildfile starting from current directory.
281Buildfile includes Makefile, build.xml etc.
282Return its file name if found, or nil if not found."
283 (or (flymake-get-buildfile-from-cache source-dir-name)
284 (let* ((file (locate-dominating-file source-dir-name buildfile-name)))
285 (if file
286 (progn
287 (flymake-log 3 "found buildfile at %s" file)
288 (flymake-add-buildfile-to-cache source-dir-name file)
289 file)
290 (progn
291 (flymake-log 3 "buildfile for %s not found" source-dir-name)
292 nil)))))
293
294(defun flymake-fix-file-name (name)
295 "Replace all occurrences of `\\' with `/'."
296 (when name
297 (setq name (expand-file-name name))
298 (setq name (abbreviate-file-name name))
299 (setq name (directory-file-name name))
300 name))
301
302(defun flymake-same-files (file-name-one file-name-two)
303 "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file.
304Return t if so, nil if not."
305 (equal (flymake-fix-file-name file-name-one)
306 (flymake-fix-file-name file-name-two)))
307
308;; This is bound dynamically to pass a parameter to a sort predicate below
309(defvar flymake-included-file-name)
310
311(defun flymake-find-possible-master-files (file-name master-file-dirs masks)
312 "Find (by name and location) all possible master files.
313
314Name is specified by FILE-NAME and location is specified by
315MASTER-FILE-DIRS. Master files include .cpp and .c for .h.
316Files are searched for starting from the .h directory and max
317max-level parent dirs. File contents are not checked."
318 (let* ((dirs master-file-dirs)
319 (files nil)
320 (done nil))
321
322 (while (and (not done) dirs)
323 (let* ((dir (expand-file-name (car dirs) (file-name-directory file-name)))
324 (masks masks))
325 (while (and (file-exists-p dir) (not done) masks)
326 (let* ((mask (car masks))
327 (dir-files (directory-files dir t mask)))
328
329 (flymake-log 3 "dir %s, %d file(s) for mask %s"
330 dir (length dir-files) mask)
331 (while (and (not done) dir-files)
332 (when (not (file-directory-p (car dir-files)))
333 (setq files (cons (car dir-files) files))
334 (when (>= (length files) flymake-master-file-count-limit)
335 (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit)
336 (setq done t)))
337 (setq dir-files (cdr dir-files))))
338 (setq masks (cdr masks))))
339 (setq dirs (cdr dirs)))
340 (when files
341 (let ((flymake-included-file-name (file-name-nondirectory file-name)))
342 (setq files (sort files 'flymake-master-file-compare))))
343 (flymake-log 3 "found %d possible master file(s)" (length files))
344 files))
345
346(defun flymake-master-file-compare (file-one file-two)
347 "Compare two files specified by FILE-ONE and FILE-TWO.
348This function is used in sort to move most possible file names
349to the beginning of the list (File.h -> File.cpp moved to top)."
350 (and (equal (file-name-sans-extension flymake-included-file-name)
351 (file-name-base file-one))
352 (not (equal file-one file-two))))
353
354(defvar flymake-check-file-limit 8192
355 "Maximum number of chars to look at when checking possible master file.
356Nil means search the entire file.")
357
358(defun flymake-check-patch-master-file-buffer
359 (master-file-temp-buffer
360 master-file-name patched-master-file-name
361 source-file-name patched-source-file-name
362 include-dirs regexp)
363 "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME.
364If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME
365instead of SOURCE-FILE-NAME.
366
367For example, foo.cpp is a master file if it includes foo.h.
368
369When a buffer for MASTER-FILE-NAME exists, use it as a source
370instead of reading master file from disk."
371 (let* ((source-file-nondir (file-name-nondirectory source-file-name))
372 (source-file-extension (file-name-extension source-file-nondir))
373 (source-file-nonext (file-name-sans-extension source-file-nondir))
374 (found nil)
375 (inc-name nil)
376 (search-limit flymake-check-file-limit))
377 (setq regexp
378 (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\""
379 ;; Hack for tex files, where \include often excludes .tex.
380 ;; Maybe this is safe generally.
381 (if (and (> (length source-file-extension) 1)
382 (string-equal source-file-extension "tex"))
383 (format "%s\\(?:\\.%s\\)?"
384 (regexp-quote source-file-nonext)
385 (regexp-quote source-file-extension))
386 (regexp-quote source-file-nondir))))
387 (unwind-protect
388 (with-current-buffer master-file-temp-buffer
389 (if (or (not search-limit)
390 (> search-limit (point-max)))
391 (setq search-limit (point-max)))
392 (flymake-log 3 "checking %s against regexp %s"
393 master-file-name regexp)
394 (goto-char (point-min))
395 (while (and (< (point) search-limit)
396 (re-search-forward regexp search-limit t))
397 (let ((match-beg (match-beginning 1))
398 (match-end (match-end 1)))
399
400 (flymake-log 3 "found possible match for %s" source-file-nondir)
401 (setq inc-name (match-string 1))
402 (and (> (length source-file-extension) 1)
403 (string-equal source-file-extension "tex")
404 (not (string-match (format "\\.%s\\'" source-file-extension)
405 inc-name))
406 (setq inc-name (concat inc-name "." source-file-extension)))
407 (when (eq t (compare-strings
408 source-file-nondir nil nil
409 inc-name (- (length inc-name)
410 (length source-file-nondir)) nil))
411 (flymake-log 3 "inc-name=%s" inc-name)
412 (when (flymake-check-include source-file-name inc-name
413 include-dirs)
414 (setq found t)
415 ;; replace-match is not used here as it fails in
416 ;; XEmacs with 'last match not a buffer' error as
417 ;; check-includes calls replace-in-string
418 (flymake-replace-region
419 match-beg match-end
420 (file-name-nondirectory patched-source-file-name))))
421 (forward-line 1)))
422 (when found
423 (flymake-save-buffer-in-file patched-master-file-name)))
424 ;;+(flymake-log 3 "killing buffer %s"
425 ;; (buffer-name master-file-temp-buffer))
426 (kill-buffer master-file-temp-buffer))
427 ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found)
428 (when found
429 (flymake-log 2 "found master file %s" master-file-name))
430 found))
431
432;;; XXX: remove
433(defun flymake-replace-region (beg end rep)
434 "Replace text in BUFFER in region (BEG END) with REP."
435 (save-excursion
436 (goto-char end)
437 ;; Insert before deleting, so as to better preserve markers's positions.
438 (insert rep)
439 (delete-region beg end)))
440
441(defun flymake-read-file-to-temp-buffer (file-name)
442 "Insert contents of FILE-NAME into newly created temp buffer."
443 (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name))))))
444 (with-current-buffer temp-buffer
445 (insert-file-contents file-name))
446 temp-buffer))
447
448(defun flymake-copy-buffer-to-temp-buffer (buffer)
449 "Copy contents of BUFFER into newly created temp buffer."
450 (with-current-buffer
451 (get-buffer-create (generate-new-buffer-name
452 (concat "flymake:" (buffer-name buffer))))
453 (insert-buffer-substring buffer)
454 (current-buffer)))
455
456(defun flymake-check-include (source-file-name inc-name include-dirs)
457 "Check if SOURCE-FILE-NAME can be found in include path.
458Return t if it can be found via include path using INC-NAME."
459 (if (file-name-absolute-p inc-name)
460 (flymake-same-files source-file-name inc-name)
461 (while (and include-dirs
462 (not (flymake-same-files
463 source-file-name
464 (concat (file-name-directory source-file-name)
465 "/" (car include-dirs)
466 "/" inc-name))))
467 (setq include-dirs (cdr include-dirs)))
468 include-dirs))
469
470(defun flymake-find-buffer-for-file (file-name)
471 "Check if there exists a buffer visiting FILE-NAME.
472Return t if so, nil if not."
473 (let ((buffer-name (get-file-buffer file-name)))
474 (if buffer-name
475 (get-buffer buffer-name))))
476
477(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp)
478 "Save SOURCE-FILE-NAME with a different name.
479Find master file, patch and save it."
480 (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks))
481 (master-file-count (length possible-master-files))
482 (idx 0)
483 (temp-buffer nil)
484 (master-file-name nil)
485 (patched-master-file-name nil)
486 (found nil))
487
488 (while (and (not found) (< idx master-file-count))
489 (setq master-file-name (nth idx possible-master-files))
490 (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master"))
491 (if (flymake-find-buffer-for-file master-file-name)
492 (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name)))
493 (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name)))
494 (setq found
495 (flymake-check-patch-master-file-buffer
496 temp-buffer
497 master-file-name
498 patched-master-file-name
499 source-file-name
500 patched-source-file-name
501 (funcall get-incl-dirs-f (file-name-directory master-file-name))
502 include-regexp))
503 (setq idx (1+ idx)))
504 (if found
505 (list master-file-name patched-master-file-name)
506 (progn
507 (flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count
508 (file-name-nondirectory source-file-name))
509 nil))))
510
511(defun flymake-save-buffer-in-file (file-name)
512 "Save the entire buffer contents into file FILE-NAME.
513Create parent directories as needed."
514 (make-directory (file-name-directory file-name) 1)
515 (write-region nil nil file-name nil 566)
516 (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name))
517
518(defun flymake-process-filter (process output)
519 "Parse OUTPUT and highlight error lines.
520It's flymake process filter."
521 (let ((source-buffer (process-buffer process)))
522
523 (flymake-log 3 "received %d byte(s) of output from process %d"
524 (length output) (process-id process))
525 (when (buffer-live-p source-buffer)
526 (with-current-buffer source-buffer
527 (flymake-parse-output-and-residual output)))))
528
529(defun flymake-process-sentinel (process _event)
530 "Sentinel for syntax check buffers."
531 (when (memq (process-status process) '(signal exit))
532 (let* ((exit-status (process-exit-status process))
533 (command (process-command process))
534 (source-buffer (process-buffer process))
535 (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer))))
536
537 (flymake-log 2 "process %d exited with code %d"
538 (process-id process) exit-status)
539 (condition-case err
540 (progn
541 (flymake-log 3 "cleaning up using %s" cleanup-f)
542 (when (buffer-live-p source-buffer)
543 (with-current-buffer source-buffer
544 (funcall cleanup-f)))
545
546 (delete-process process)
547 (setq flymake-processes (delq process flymake-processes))
548
549 (when (buffer-live-p source-buffer)
550 (with-current-buffer source-buffer
551
552 (flymake-parse-residual)
553 (flymake-post-syntax-check exit-status command)
554 (setq flymake-is-running nil))))
555 (error
556 (let ((err-str (format "Error in process sentinel for buffer %s: %s"
557 source-buffer (error-message-string err))))
558 (flymake-log 0 err-str)
559 (with-current-buffer source-buffer
560 (setq flymake-is-running nil))))))))
561
562(defun flymake-post-syntax-check (exit-status command)
563 (save-restriction
564 (widen)
565 (setq flymake-err-info flymake-new-err-info)
566 (setq flymake-new-err-info nil)
567 (setq flymake-err-info
568 (flymake-fix-line-numbers
569 flymake-err-info 1 (count-lines (point-min) (point-max))))
570 (flymake-delete-own-overlays)
571 (flymake-highlight-err-lines flymake-err-info)
572 (let (err-count warn-count)
573 (setq err-count (flymake-get-err-count flymake-err-info "e"))
574 (setq warn-count (flymake-get-err-count flymake-err-info "w"))
575 (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)"
576 (buffer-name) err-count warn-count
577 (- (float-time) flymake-check-start-time))
578 (setq flymake-check-start-time nil)
579
580 (if (and (equal 0 err-count) (equal 0 warn-count))
581 (if (equal 0 exit-status)
582 (flymake-report-status "" "") ; PASSED
583 (if (not flymake-check-was-interrupted)
584 (flymake-report-fatal-status "CFGERR"
585 (format "Configuration error has occurred while running %s" command))
586 (flymake-report-status nil ""))) ; "STOPPED"
587 (flymake-report-status (format "%d/%d" err-count warn-count) "")))))
588
589(defun flymake-parse-output-and-residual (output)
590 "Split OUTPUT into lines, merge in residual if necessary."
591 (let* ((buffer-residual flymake-output-residual)
592 (total-output (if buffer-residual (concat buffer-residual output) output))
593 (lines-and-residual (flymake-split-output total-output))
594 (lines (nth 0 lines-and-residual))
595 (new-residual (nth 1 lines-and-residual)))
596 (setq flymake-output-residual new-residual)
597 (setq flymake-new-err-info
598 (flymake-parse-err-lines
599 flymake-new-err-info lines))))
600
601(defun flymake-parse-residual ()
602 "Parse residual if it's non empty."
603 (when flymake-output-residual
604 (setq flymake-new-err-info
605 (flymake-parse-err-lines
606 flymake-new-err-info
607 (list flymake-output-residual)))
608 (setq flymake-output-residual nil)))
609
610(defun flymake-er-make-er (line-no line-err-info-list)
611 (list line-no line-err-info-list))
612
613(defun flymake-er-get-line (err-info)
614 (nth 0 err-info))
615
616(defun flymake-er-get-line-err-info-list (err-info)
617 (nth 1 err-info))
618
619(cl-defstruct (flymake-ler
620 (:constructor nil)
621 (:constructor flymake-ler-make-ler (file line type text &optional full-file)))
622 file line type text full-file)
623
624(defun flymake-ler-set-file (line-err-info file)
625 (flymake-ler-make-ler file
626 (flymake-ler-line line-err-info)
627 (flymake-ler-type line-err-info)
628 (flymake-ler-text line-err-info)
629 (flymake-ler-full-file line-err-info)))
630
631(defun flymake-ler-set-full-file (line-err-info full-file)
632 (flymake-ler-make-ler (flymake-ler-file line-err-info)
633 (flymake-ler-line line-err-info)
634 (flymake-ler-type line-err-info)
635 (flymake-ler-text line-err-info)
636 full-file))
637
638(defun flymake-ler-set-line (line-err-info line)
639 (flymake-ler-make-ler (flymake-ler-file line-err-info)
640 line
641 (flymake-ler-type line-err-info)
642 (flymake-ler-text line-err-info)
643 (flymake-ler-full-file line-err-info)))
644
645(defun flymake-get-line-err-count (line-err-info-list type)
646 "Return number of errors of specified TYPE.
647Value of TYPE is either \"e\" or \"w\"."
648 (let* ((idx 0)
649 (count (length line-err-info-list))
650 (err-count 0))
651
652 (while (< idx count)
653 (when (equal type (flymake-ler-type (nth idx line-err-info-list)))
654 (setq err-count (1+ err-count)))
655 (setq idx (1+ idx)))
656 err-count))
657
658(defun flymake-get-err-count (err-info-list type)
659 "Return number of errors of specified TYPE for ERR-INFO-LIST."
660 (let* ((idx 0)
661 (count (length err-info-list))
662 (err-count 0))
663 (while (< idx count)
664 (setq err-count (+ err-count (flymake-get-line-err-count (nth 1 (nth idx err-info-list)) type)))
665 (setq idx (1+ idx)))
666 err-count))
667
668(defun flymake-fix-line-numbers (err-info-list min-line max-line)
669 "Replace line numbers with fixed value.
670If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE.
671If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE.
672The reason for this fix is because some compilers might report
673line number outside the file being compiled."
674 (let* ((count (length err-info-list))
675 (err-info nil)
676 (line 0))
677 (while (> count 0)
678 (setq err-info (nth (1- count) err-info-list))
679 (setq line (flymake-er-get-line err-info))
680 (when (or (< line min-line) (> line max-line))
681 (setq line (if (< line min-line) min-line max-line))
682 (setq err-info-list (flymake-set-at err-info-list (1- count)
683 (flymake-er-make-er line
684 (flymake-er-get-line-err-info-list err-info)))))
685 (setq count (1- count))))
686 err-info-list)
687
688(defun flymake-highlight-err-lines (err-info-list)
689 "Highlight error lines in BUFFER using info from ERR-INFO-LIST."
690 (save-excursion
691 (dolist (err err-info-list)
692 (flymake-highlight-line (car err) (nth 1 err)))))
693
694(defun flymake-overlay-p (ov)
695 "Determine whether overlay OV was created by flymake."
696 (and (overlayp ov) (overlay-get ov 'flymake-overlay)))
697
698(defun flymake-make-overlay (beg end tooltip-text face bitmap)
699 "Allocate a flymake overlay in range BEG and END."
700 (when (not (flymake-region-has-flymake-overlays beg end))
701 (let ((ov (make-overlay beg end nil t))
702 (fringe (and flymake-fringe-indicator-position
703 (propertize "!" 'display
704 (cons flymake-fringe-indicator-position
705 (if (listp bitmap)
706 bitmap
707 (list bitmap)))))))
708 (overlay-put ov 'face face)
709 (overlay-put ov 'help-echo tooltip-text)
710 (overlay-put ov 'flymake-overlay t)
711 (overlay-put ov 'priority 100)
712 (overlay-put ov 'evaporate t)
713 (overlay-put ov 'before-string fringe)
714 ;;+(flymake-log 3 "created overlay %s" ov)
715 ov)
716 (flymake-log 3 "created an overlay at (%d-%d)" beg end)))
717
718(defun flymake-delete-own-overlays ()
719 "Delete all flymake overlays in BUFFER."
720 (dolist (ol (overlays-in (point-min) (point-max)))
721 (when (flymake-overlay-p ol)
722 (delete-overlay ol)
723 ;;+(flymake-log 3 "deleted overlay %s" ol)
724 )))
725
726(defun flymake-region-has-flymake-overlays (beg end)
727 "Check if region specified by BEG and END has overlay.
728Return t if it has at least one flymake overlay, nil if no overlay."
729 (let ((ov (overlays-in beg end))
730 (has-flymake-overlays nil))
731 (while (consp ov)
732 (when (flymake-overlay-p (car ov))
733 (setq has-flymake-overlays t))
734 (setq ov (cdr ov)))
735 has-flymake-overlays))
736
737(defface flymake-errline
738 '((((supports :underline (:style wave)))
739 :underline (:style wave :color "Red1"))
740 (t
741 :inherit error))
742 "Face used for marking error lines."
743 :version "24.4"
744 :group 'flymake)
745
746(defface flymake-warnline
747 '((((supports :underline (:style wave)))
748 :underline (:style wave :color "DarkOrange"))
749 (t
750 :inherit warning))
751 "Face used for marking warning lines."
752 :version "24.4"
753 :group 'flymake)
754
755(defun flymake-highlight-line (line-no line-err-info-list)
756 "Highlight line LINE-NO in current buffer.
757Perhaps use text from LINE-ERR-INFO-LIST to enhance highlighting."
758 (goto-char (point-min))
759 (forward-line (1- line-no))
760 (pcase-let* ((beg (progn (back-to-indentation) (point)))
761 (end (progn
762 (end-of-line)
763 (skip-chars-backward " \t\f\t\n" beg)
764 (if (eq (point) beg)
765 (line-beginning-position 2)
766 (point))))
767 (tooltip-text (mapconcat #'flymake-ler-text line-err-info-list "\n"))
768 (`(,face ,bitmap)
769 (if (> (flymake-get-line-err-count line-err-info-list "e") 0)
770 (list 'flymake-errline flymake-error-bitmap)
771 (list 'flymake-warnline flymake-warning-bitmap))))
772 (flymake-make-overlay beg end tooltip-text face bitmap)))
773
774(defun flymake-parse-err-lines (err-info-list lines)
775 "Parse err LINES, store info in ERR-INFO-LIST."
776 (let* ((count (length lines))
777 (idx 0)
778 (line-err-info nil)
779 (real-file-name nil)
780 (source-file-name buffer-file-name)
781 (get-real-file-name-f (flymake-get-real-file-name-function source-file-name)))
782
783 (while (< idx count)
784 (setq line-err-info (flymake-parse-line (nth idx lines)))
785 (when line-err-info
786 (setq real-file-name (funcall get-real-file-name-f
787 (flymake-ler-file line-err-info)))
788 (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name))
789
790 (when (flymake-same-files real-file-name source-file-name)
791 (setq line-err-info (flymake-ler-set-file line-err-info nil))
792 (setq err-info-list (flymake-add-err-info err-info-list line-err-info))))
793 (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no"))
794 (setq idx (1+ idx)))
795 err-info-list))
796
797(defun flymake-split-output (output)
798 "Split OUTPUT into lines.
799Return last one as residual if it does not end with newline char.
800Returns ((LINES) RESIDUAL)."
801 (when (and output (> (length output) 0))
802 (let* ((lines (split-string output "[\n\r]+" t))
803 (complete (equal "\n" (char-to-string (aref output (1- (length output))))))
804 (residual nil))
805 (when (not complete)
806 (setq residual (car (last lines)))
807 (setq lines (butlast lines)))
808 (list lines residual))))
809
810(defun flymake-reformat-err-line-patterns-from-compile-el (original-list)
811 "Grab error line patterns from ORIGINAL-LIST in compile.el format.
812Convert it to flymake internal format."
813 (let* ((converted-list '()))
814 (dolist (item original-list)
815 (setq item (cdr item))
816 (let ((regexp (nth 0 item))
817 (file (nth 1 item))
818 (line (nth 2 item))
819 (col (nth 3 item)))
820 (if (consp file) (setq file (car file)))
821 (if (consp line) (setq line (car line)))
822 (if (consp col) (setq col (car col)))
823
824 (when (not (functionp line))
825 (setq converted-list (cons (list regexp file line col) converted-list)))))
826 converted-list))
827
828(require 'compile)
829
830(defvar flymake-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text
831 (append
832 '(
833 ;; MS Visual C++ 6.0
834 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
835 1 3 nil 4)
836 ;; jikes
837 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)"
838 1 3 nil 4)
839 ;; MS midl
840 ("midl[ ]*:[ ]*\\(command line error .*\\)"
841 nil nil nil 1)
842 ;; MS C#
843 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
844 1 3 nil 4)
845 ;; perl
846 ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1)
847 ;; PHP
848 ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1)
849 ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1)
850 ;; ant/javac. Note this also matches gcc warnings!
851 (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)"
852 2 4 nil 5))
853 ;; compilation-error-regexp-alist)
854 (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist))
855 "Patterns for matching error/warning lines. Each pattern has the form
856\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX).
857Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns
858from compile.el")
859
860(define-obsolete-variable-alias 'flymake-warning-re 'flymake-warning-predicate "24.4")
861(defvar flymake-warning-predicate "^[wW]arning"
862 "Predicate matching against error text to detect a warning.
863Takes a single argument, the error's text and should return non-nil
864if it's a warning.
865Instead of a function, it can also be a regular expression.")
866
867(defun flymake-parse-line (line)
868 "Parse LINE to see if it is an error or warning.
869Return its components if so, nil otherwise."
870 (let ((raw-file-name nil)
871 (line-no 0)
872 (err-type "e")
873 (err-text nil)
874 (patterns flymake-err-line-patterns)
875 (matched nil))
876 (while (and patterns (not matched))
877 (when (string-match (car (car patterns)) line)
878 (let* ((file-idx (nth 1 (car patterns)))
879 (line-idx (nth 2 (car patterns))))
880
881 (setq raw-file-name (if file-idx (match-string file-idx line) nil))
882 (setq line-no (if line-idx (string-to-number
883 (match-string line-idx line)) 0))
884 (setq err-text (if (> (length (car patterns)) 4)
885 (match-string (nth 4 (car patterns)) line)
886 (flymake-patch-err-text
887 (substring line (match-end 0)))))
888 (if (null err-text)
889 (setq err-text "<no error text>")
890 (when (cond ((stringp flymake-warning-predicate)
891 (string-match flymake-warning-predicate err-text))
892 ((functionp flymake-warning-predicate)
893 (funcall flymake-warning-predicate err-text)))
894 (setq err-type "w")))
895 (flymake-log
896 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s"
897 file-idx line-idx raw-file-name line-no err-text)
898 (setq matched t)))
899 (setq patterns (cdr patterns)))
900 (if matched
901 (flymake-ler-make-ler raw-file-name line-no err-type err-text)
902 ())))
903
904(defun flymake-find-err-info (err-info-list line-no)
905 "Find (line-err-info-list pos) for specified LINE-NO."
906 (if err-info-list
907 (let* ((line-err-info-list nil)
908 (pos 0)
909 (count (length err-info-list)))
910
911 (while (and (< pos count) (< (car (nth pos err-info-list)) line-no))
912 (setq pos (1+ pos)))
913 (when (and (< pos count) (equal (car (nth pos err-info-list)) line-no))
914 (setq line-err-info-list (flymake-er-get-line-err-info-list (nth pos err-info-list))))
915 (list line-err-info-list pos))
916 '(nil 0)))
917
918(defun flymake-line-err-info-is-less-or-equal (line-one line-two)
919 (or (string< (flymake-ler-type line-one) (flymake-ler-type line-two))
920 (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two))
921 (not (flymake-ler-file line-one)) (flymake-ler-file line-two))
922 (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two))
923 (or (and (flymake-ler-file line-one) (flymake-ler-file line-two))
924 (and (not (flymake-ler-file line-one)) (not (flymake-ler-file line-two)))))))
925
926(defun flymake-add-line-err-info (line-err-info-list line-err-info)
927 "Update LINE-ERR-INFO-LIST with the error LINE-ERR-INFO.
928For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'.
929The new element is inserted in the proper position, according to
930the predicate `flymake-line-err-info-is-less-or-equal'.
931The updated value of LINE-ERR-INFO-LIST is returned."
932 (if (not line-err-info-list)
933 (list line-err-info)
934 (let* ((count (length line-err-info-list))
935 (idx 0))
936 (while (and (< idx count) (flymake-line-err-info-is-less-or-equal (nth idx line-err-info-list) line-err-info))
937 (setq idx (1+ idx)))
938 (cond ((equal 0 idx) (setq line-err-info-list (cons line-err-info line-err-info-list)))
939 (t (setq line-err-info-list (flymake-ins-after line-err-info-list (1- idx) line-err-info))))
940 line-err-info-list)))
941
942(defun flymake-add-err-info (err-info-list line-err-info)
943 "Update ERR-INFO-LIST with the error LINE-ERR-INFO, preserving sort order.
944Returns the updated value of ERR-INFO-LIST.
945For the format of ERR-INFO-LIST, see `flymake-err-info'.
946For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'."
947 (let* ((line-no (if (flymake-ler-file line-err-info) 1 (flymake-ler-line line-err-info)))
948 (info-and-pos (flymake-find-err-info err-info-list line-no))
949 (exists (car info-and-pos))
950 (pos (nth 1 info-and-pos))
951 (line-err-info-list nil)
952 (err-info nil))
953
954 (if exists
955 (setq line-err-info-list (flymake-er-get-line-err-info-list (car (nthcdr pos err-info-list)))))
956 (setq line-err-info-list (flymake-add-line-err-info line-err-info-list line-err-info))
957
958 (setq err-info (flymake-er-make-er line-no line-err-info-list))
959 (cond (exists (setq err-info-list (flymake-set-at err-info-list pos err-info)))
960 ((equal 0 pos) (setq err-info-list (cons err-info err-info-list)))
961 (t (setq err-info-list (flymake-ins-after err-info-list (1- pos) err-info))))
962 err-info-list))
963
964(defun flymake-get-project-include-dirs-imp (basedir)
965 "Include dirs for the project current file belongs to."
966 (if (flymake-get-project-include-dirs-from-cache basedir)
967 (progn
968 (flymake-get-project-include-dirs-from-cache basedir))
969 ;;else
970 (let* ((command-line (concat "make -C "
971 (shell-quote-argument basedir)
972 " DUMPVARS=INCLUDE_DIRS dumpvars"))
973 (output (shell-command-to-string command-line))
974 (lines (split-string output "\n" t))
975 (count (length lines))
976 (idx 0)
977 (inc-dirs nil))
978 (while (and (< idx count) (not (string-match "^INCLUDE_DIRS=.*" (nth idx lines))))
979 (setq idx (1+ idx)))
980 (when (< idx count)
981 (let* ((inc-lines (split-string (nth idx lines) " *-I" t))
982 (inc-count (length inc-lines)))
983 (while (> inc-count 0)
984 (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines)))
985 (push (replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs))
986 (setq inc-count (1- inc-count)))))
987 (flymake-add-project-include-dirs-to-cache basedir inc-dirs)
988 inc-dirs)))
989
990(defvar flymake-get-project-include-dirs-function #'flymake-get-project-include-dirs-imp
991 "Function used to get project include dirs, one parameter: basedir name.")
992
993(defun flymake-get-project-include-dirs (basedir)
994 (funcall flymake-get-project-include-dirs-function basedir))
995
996(defun flymake-get-system-include-dirs ()
997 "System include dirs - from the `INCLUDE' env setting."
998 (let* ((includes (getenv "INCLUDE")))
999 (if includes (split-string includes path-separator t) nil)))
1000
1001(defvar flymake-project-include-dirs-cache (make-hash-table :test #'equal))
1002
1003(defun flymake-get-project-include-dirs-from-cache (base-dir)
1004 (gethash base-dir flymake-project-include-dirs-cache))
1005
1006(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs)
1007 (puthash base-dir include-dirs flymake-project-include-dirs-cache))
1008
1009(defun flymake-clear-project-include-dirs-cache ()
1010 (clrhash flymake-project-include-dirs-cache))
1011
1012(defun flymake-get-include-dirs (base-dir)
1013 "Get dirs to use when resolving local file names."
1014 (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs))))
1015 include-dirs))
1016
1017;; (defun flymake-restore-formatting ()
1018;; "Remove any formatting made by flymake."
1019;; )
1020
1021;; (defun flymake-get-program-dir (buffer)
1022;; "Get dir to start program in."
1023;; (unless (bufferp buffer)
1024;; (error "Invalid buffer"))
1025;; (with-current-buffer buffer
1026;; default-directory))
1027
1028(defun flymake-safe-delete-file (file-name)
1029 (when (and file-name (file-exists-p file-name))
1030 (delete-file file-name)
1031 (flymake-log 1 "deleted file %s" file-name)))
1032
1033(defun flymake-safe-delete-directory (dir-name)
1034 (condition-case nil
1035 (progn
1036 (delete-directory dir-name)
1037 (flymake-log 1 "deleted dir %s" dir-name))
1038 (error
1039 (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name))))
1040
1041(defun flymake-start-syntax-check ()
1042 "Start syntax checking for current buffer."
1043 (interactive)
1044 (flymake-log 3 "flymake is running: %s" flymake-is-running)
1045 (when (and (not flymake-is-running)
1046 (flymake-can-syntax-check-file buffer-file-name))
1047 (when (or (not flymake-compilation-prevents-syntax-check)
1048 (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP")
1049 (flymake-clear-buildfile-cache)
1050 (flymake-clear-project-include-dirs-cache)
1051
1052 (setq flymake-check-was-interrupted nil)
1053
1054 (let* ((source-file-name buffer-file-name)
1055 (init-f (flymake-get-init-function source-file-name))
1056 (cleanup-f (flymake-get-cleanup-function source-file-name))
1057 (cmd-and-args (funcall init-f))
1058 (cmd (nth 0 cmd-and-args))
1059 (args (nth 1 cmd-and-args))
1060 (dir (nth 2 cmd-and-args)))
1061 (if (not cmd-and-args)
1062 (progn
1063 (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name)
1064 (funcall cleanup-f))
1065 (progn
1066 (setq flymake-last-change-time nil)
1067 (flymake-start-syntax-check-process cmd args dir)))))))
1068
1069(defun flymake-start-syntax-check-process (cmd args dir)
1070 "Start syntax check process."
1071 (condition-case err
1072 (let* ((process
1073 (let ((default-directory (or dir default-directory)))
1074 (when dir
1075 (flymake-log 3 "starting process on dir %s" dir))
1076 (apply 'start-file-process
1077 "flymake-proc" (current-buffer) cmd args))))
1078 (set-process-sentinel process 'flymake-process-sentinel)
1079 (set-process-filter process 'flymake-process-filter)
1080 (set-process-query-on-exit-flag process nil)
1081 (push process flymake-processes)
1082
1083 (setq flymake-is-running t)
1084 (setq flymake-last-change-time nil)
1085 (setq flymake-check-start-time (float-time))
1086
1087 (flymake-report-status nil "*")
1088 (flymake-log 2 "started process %d, command=%s, dir=%s"
1089 (process-id process) (process-command process)
1090 default-directory)
1091 process)
1092 (error
1093 (let* ((err-str
1094 (format-message
1095 "Failed to launch syntax check process `%s' with args %s: %s"
1096 cmd args (error-message-string err)))
1097 (source-file-name buffer-file-name)
1098 (cleanup-f (flymake-get-cleanup-function source-file-name)))
1099 (flymake-log 0 err-str)
1100 (funcall cleanup-f)
1101 (flymake-report-fatal-status "PROCERR" err-str)))))
1102
1103(defun flymake-kill-process (proc)
1104 "Kill process PROC."
1105 (kill-process proc)
1106 (let* ((buf (process-buffer proc)))
1107 (when (buffer-live-p buf)
1108 (with-current-buffer buf
1109 (setq flymake-check-was-interrupted t))))
1110 (flymake-log 1 "killed process %d" (process-id proc)))
1111
1112(defun flymake-stop-all-syntax-checks ()
1113 "Kill all syntax check processes."
1114 (interactive)
1115 (while flymake-processes
1116 (flymake-kill-process (pop flymake-processes))))
1117
1118(defun flymake-compilation-is-running ()
1119 (and (boundp 'compilation-in-progress)
1120 compilation-in-progress))
1121
1122(defun flymake-compile ()
1123 "Kill all flymake syntax checks, start compilation."
1124 (interactive)
1125 (flymake-stop-all-syntax-checks)
1126 (call-interactively 'compile))
1127
1128(defun flymake-on-timer-event (buffer)
1129 "Start a syntax check for buffer BUFFER if necessary."
1130 (when (buffer-live-p buffer)
1131 (with-current-buffer buffer
1132 (when (and (not flymake-is-running)
1133 flymake-last-change-time
1134 (> (- (float-time) flymake-last-change-time)
1135 flymake-no-changes-timeout))
1136
1137 (setq flymake-last-change-time nil)
1138 (flymake-log 3 "starting syntax check as more than 1 second passed since last change")
1139 (flymake-start-syntax-check)))))
1140
1141(define-obsolete-function-alias 'flymake-display-err-menu-for-current-line
1142 'flymake-popup-current-error-menu "24.4")
1143
1144(defun flymake-popup-current-error-menu (&optional event)
1145 "Pop up a menu with errors/warnings for current line."
1146 (interactive (list last-nonmenu-event))
1147 (let* ((line-no (line-number-at-pos))
1148 (errors (or (car (flymake-find-err-info flymake-err-info line-no))
1149 (user-error "No errors for current line")))
1150 (menu (mapcar (lambda (x)
1151 (if (flymake-ler-file x)
1152 (cons (format "%s - %s(%d)"
1153 (flymake-ler-text x)
1154 (flymake-ler-file x)
1155 (flymake-ler-line x))
1156 x)
1157 (list (flymake-ler-text x))))
1158 errors))
1159 (event (if (mouse-event-p event)
1160 event
1161 (list 'mouse-1 (posn-at-point))))
1162 (title (format "Line %d: %d error(s), %d warning(s)"
1163 line-no
1164 (flymake-get-line-err-count errors "e")
1165 (flymake-get-line-err-count errors "w")))
1166 (choice (x-popup-menu event (list title (cons "" menu)))))
1167 (flymake-log 3 "choice=%s" choice)
1168 (when choice
1169 (flymake-goto-file-and-line (flymake-ler-full-file choice)
1170 (flymake-ler-line choice)))))
1171
1172(defun flymake-goto-file-and-line (file line)
1173 "Try to get buffer for FILE and goto line LINE in it."
1174 (if (not (file-exists-p file))
1175 (flymake-log 1 "File %s does not exist" file)
1176 (find-file file)
1177 (goto-char (point-min))
1178 (forward-line (1- line))))
1179
1180;; flymake minor mode declarations
1181(defvar-local flymake-mode-line nil)
1182(defvar-local flymake-mode-line-e-w nil)
1183(defvar-local flymake-mode-line-status nil)
1184
1185(defun flymake-report-status (e-w &optional status)
1186 "Show status in mode line."
1187 (when e-w
1188 (setq flymake-mode-line-e-w e-w))
1189 (when status
1190 (setq flymake-mode-line-status status))
1191 (let* ((mode-line " Flymake"))
1192 (when (> (length flymake-mode-line-e-w) 0)
1193 (setq mode-line (concat mode-line ":" flymake-mode-line-e-w)))
1194 (setq mode-line (concat mode-line flymake-mode-line-status))
1195 (setq flymake-mode-line mode-line)
1196 (force-mode-line-update)))
1197
1198;; Nothing in flymake uses this at all any more, so this is just for
1199;; third-party compatibility.
1200(define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1")
1201
1202(defun flymake-report-fatal-status (status warning)
1203 "Display a warning and switch flymake mode off."
1204 ;; This first message was always shown by default, and flymake-log
1205 ;; does nothing by default, hence the use of message.
1206 ;; Another option is display-warning.
1207 (if (< flymake-log-level 0)
1208 (message "Flymake: %s. Flymake will be switched OFF" warning))
1209 (flymake-mode 0)
1210 (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s"
1211 (buffer-name) status warning))
1212
1213;;;###autoload
1214(define-minor-mode flymake-mode nil
1215 :group 'flymake :lighter flymake-mode-line
1216 (cond
1217
1218 ;; Turning the mode ON.
1219 (flymake-mode
1220 (cond
1221 ((not buffer-file-name)
1222 (message "Flymake unable to run without a buffer file name"))
1223 ((not (flymake-can-syntax-check-file buffer-file-name))
1224 (flymake-log 2 "flymake cannot check syntax in buffer %s" (buffer-name)))
1225 (t
1226 (add-hook 'after-change-functions 'flymake-after-change-function nil t)
1227 (add-hook 'after-save-hook 'flymake-after-save-hook nil t)
1228 (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t)
1229 ;;+(add-hook 'find-file-hook 'flymake-find-file-hook)
1230
1231 (flymake-report-status "" "")
1232
1233 (setq flymake-timer
1234 (run-at-time nil 1 'flymake-on-timer-event (current-buffer)))
1235
1236 (when (and flymake-start-syntax-check-on-find-file
1237 ;; Since we write temp files in current dir, there's no point
1238 ;; trying if the directory is read-only (bug#8954).
1239 (file-writable-p (file-name-directory buffer-file-name)))
1240 (with-demoted-errors
1241 (flymake-start-syntax-check))))))
1242
1243 ;; Turning the mode OFF.
1244 (t
1245 (remove-hook 'after-change-functions 'flymake-after-change-function t)
1246 (remove-hook 'after-save-hook 'flymake-after-save-hook t)
1247 (remove-hook 'kill-buffer-hook 'flymake-kill-buffer-hook t)
1248 ;;+(remove-hook 'find-file-hook (function flymake-find-file-hook) t)
1249
1250 (flymake-delete-own-overlays)
1251
1252 (when flymake-timer
1253 (cancel-timer flymake-timer)
1254 (setq flymake-timer nil))
1255
1256 (setq flymake-is-running nil))))
1257
1258;;;###autoload
1259(defun flymake-mode-on ()
1260 "Turn flymake mode on."
1261 (flymake-mode 1)
1262 (flymake-log 1 "flymake mode turned ON for buffer %s" (buffer-name)))
1263
1264;;;###autoload
1265(defun flymake-mode-off ()
1266 "Turn flymake mode off."
1267 (flymake-mode 0)
1268 (flymake-log 1 "flymake mode turned OFF for buffer %s" (buffer-name)))
1269
1270(defun flymake-after-change-function (start stop _len)
1271 "Start syntax check for current buffer if it isn't already running."
1272 ;;+(flymake-log 0 "setting change time to %s" (float-time))
1273 (let((new-text (buffer-substring start stop)))
1274 (when (and flymake-start-syntax-check-on-newline (equal new-text "\n"))
1275 (flymake-log 3 "starting syntax check as new-line has been seen")
1276 (flymake-start-syntax-check))
1277 (setq flymake-last-change-time (float-time))))
1278
1279(defun flymake-after-save-hook ()
1280 (if (local-variable-p 'flymake-mode (current-buffer)) ; (???) other way to determine whether flymake is active in buffer being saved?
1281 (progn
1282 (flymake-log 3 "starting syntax check as buffer was saved")
1283 (flymake-start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???)
1284
1285(defun flymake-kill-buffer-hook ()
1286 (when flymake-timer
1287 (cancel-timer flymake-timer)
1288 (setq flymake-timer nil)))
1289
1290;;;###autoload
1291(defun flymake-find-file-hook ()
1292 ;;+(when flymake-start-syntax-check-on-find-file
1293 ;;+ (flymake-log 3 "starting syntax check on file open")
1294 ;;+ (flymake-start-syntax-check)
1295 ;;+)
1296 (when (and (not (local-variable-p 'flymake-mode (current-buffer)))
1297 (flymake-can-syntax-check-file buffer-file-name))
1298 (flymake-mode)
1299 (flymake-log 3 "automatically turned ON flymake mode")))
1300
1301(defun flymake-get-first-err-line-no (err-info-list)
1302 "Return first line with error."
1303 (when err-info-list
1304 (flymake-er-get-line (car err-info-list))))
1305
1306(defun flymake-get-last-err-line-no (err-info-list)
1307 "Return last line with error."
1308 (when err-info-list
1309 (flymake-er-get-line (nth (1- (length err-info-list)) err-info-list))))
1310
1311(defun flymake-get-next-err-line-no (err-info-list line-no)
1312 "Return next line with error."
1313 (when err-info-list
1314 (let* ((count (length err-info-list))
1315 (idx 0))
1316 (while (and (< idx count) (>= line-no (flymake-er-get-line (nth idx err-info-list))))
1317 (setq idx (1+ idx)))
1318 (if (< idx count)
1319 (flymake-er-get-line (nth idx err-info-list))))))
1320
1321(defun flymake-get-prev-err-line-no (err-info-list line-no)
1322 "Return previous line with error."
1323 (when err-info-list
1324 (let* ((count (length err-info-list)))
1325 (while (and (> count 0) (<= line-no (flymake-er-get-line (nth (1- count) err-info-list))))
1326 (setq count (1- count)))
1327 (if (> count 0)
1328 (flymake-er-get-line (nth (1- count) err-info-list))))))
1329
1330(defun flymake-skip-whitespace ()
1331 "Move forward until non-whitespace is reached."
1332 (while (looking-at "[ \t]")
1333 (forward-char)))
1334
1335(defun flymake-goto-line (line-no)
1336 "Go to line LINE-NO, then skip whitespace."
1337 (goto-char (point-min))
1338 (forward-line (1- line-no))
1339 (flymake-skip-whitespace))
1340
1341(defun flymake-goto-next-error ()
1342 "Go to next error in err ring."
1343 (interactive)
1344 (let ((line-no (flymake-get-next-err-line-no flymake-err-info (line-number-at-pos))))
1345 (when (not line-no)
1346 (setq line-no (flymake-get-first-err-line-no flymake-err-info))
1347 (flymake-log 1 "passed end of file"))
1348 (if line-no
1349 (flymake-goto-line line-no)
1350 (flymake-log 1 "no errors in current buffer"))))
1351
1352(defun flymake-goto-prev-error ()
1353 "Go to previous error in err ring."
1354 (interactive)
1355 (let ((line-no (flymake-get-prev-err-line-no flymake-err-info (line-number-at-pos))))
1356 (when (not line-no)
1357 (setq line-no (flymake-get-last-err-line-no flymake-err-info))
1358 (flymake-log 1 "passed beginning of file"))
1359 (if line-no
1360 (flymake-goto-line line-no)
1361 (flymake-log 1 "no errors in current buffer"))))
1362
1363(defun flymake-patch-err-text (string)
1364 (if (string-match "^[\n\t :0-9]*\\(.*\\)$" string)
1365 (match-string 1 string)
1366 string))
1367
1368;;;; general init-cleanup and helper routines
1369(defun flymake-create-temp-inplace (file-name prefix)
1370 (unless (stringp file-name)
1371 (error "Invalid file-name"))
1372 (or prefix
1373 (setq prefix "flymake"))
1374 (let* ((ext (file-name-extension file-name))
1375 (temp-name (file-truename
1376 (concat (file-name-sans-extension file-name)
1377 "_" prefix
1378 (and ext (concat "." ext))))))
1379 (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name)
1380 temp-name))
1381
1382(defun flymake-create-temp-with-folder-structure (file-name _prefix)
1383 (unless (stringp file-name)
1384 (error "Invalid file-name"))
1385
1386 (let* ((dir (file-name-directory file-name))
1387 ;; Not sure what this slash-pos is all about, but I guess it's just
1388 ;; trying to remove the leading / of absolute file names.
1389 (slash-pos (string-match "/" dir))
1390 (temp-dir (expand-file-name (substring dir (1+ slash-pos))
1391 temporary-file-directory)))
1392
1393 (file-truename (expand-file-name (file-name-nondirectory file-name)
1394 temp-dir))))
1395
1396(defun flymake-delete-temp-directory (dir-name)
1397 "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error."
1398 (let* ((temp-dir temporary-file-directory)
1399 (suffix (substring dir-name (1+ (length temp-dir)))))
1400
1401 (while (> (length suffix) 0)
1402 (setq suffix (directory-file-name suffix))
1403 ;;+(flymake-log 0 "suffix=%s" suffix)
1404 (flymake-safe-delete-directory
1405 (file-truename (expand-file-name suffix temp-dir)))
1406 (setq suffix (file-name-directory suffix)))))
1407
1408(defvar-local flymake-temp-source-file-name nil)
1409(defvar-local flymake-master-file-name nil)
1410(defvar-local flymake-temp-master-file-name nil)
1411(defvar-local flymake-base-dir nil)
1412
1413(defun flymake-init-create-temp-buffer-copy (create-temp-f)
1414 "Make a temporary copy of the current buffer, save its name in buffer data and return the name."
1415 (let* ((source-file-name buffer-file-name)
1416 (temp-source-file-name (funcall create-temp-f source-file-name "flymake")))
1417
1418 (flymake-save-buffer-in-file temp-source-file-name)
1419 (setq flymake-temp-source-file-name temp-source-file-name)
1420 temp-source-file-name))
1421
1422(defun flymake-simple-cleanup ()
1423 "Do cleanup after `flymake-init-create-temp-buffer-copy'.
1424Delete temp file."
1425 (flymake-safe-delete-file flymake-temp-source-file-name)
1426 (setq flymake-last-change-time nil))
1427
1428(defun flymake-get-real-file-name (file-name-from-err-msg)
1429 "Translate file name from error message to \"real\" file name.
1430Return full-name. Names are real, not patched."
1431 (let* ((real-name nil)
1432 (source-file-name buffer-file-name)
1433 (master-file-name flymake-master-file-name)
1434 (temp-source-file-name flymake-temp-source-file-name)
1435 (temp-master-file-name flymake-temp-master-file-name)
1436 (base-dirs
1437 (list flymake-base-dir
1438 (file-name-directory source-file-name)
1439 (if master-file-name (file-name-directory master-file-name))))
1440 (files (list (list source-file-name source-file-name)
1441 (list temp-source-file-name source-file-name)
1442 (list master-file-name master-file-name)
1443 (list temp-master-file-name master-file-name))))
1444
1445 (when (equal 0 (length file-name-from-err-msg))
1446 (setq file-name-from-err-msg source-file-name))
1447
1448 (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files))
1449 ;; if real-name is nil, than file name from err msg is none of the files we've patched
1450 (if (not real-name)
1451 (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs)))
1452 (if (not real-name)
1453 (setq real-name file-name-from-err-msg))
1454 (setq real-name (flymake-fix-file-name real-name))
1455 (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name)
1456 real-name))
1457
1458(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files)
1459 (let* ((base-dirs-count (length base-dirs))
1460 (file-count (length files))
1461 (real-name nil))
1462
1463 (while (and (not real-name) (> base-dirs-count 0))
1464 (setq file-count (length files))
1465 (while (and (not real-name) (> file-count 0))
1466 (let* ((this-dir (nth (1- base-dirs-count) base-dirs))
1467 (this-file (nth 0 (nth (1- file-count) files)))
1468 (this-real-name (nth 1 (nth (1- file-count) files))))
1469 ;;+(flymake-log 0 "this-dir=%s this-file=%s this-real=%s msg-file=%s" this-dir this-file this-real-name file-name-from-err-msg)
1470 (when (and this-dir this-file (flymake-same-files
1471 (expand-file-name file-name-from-err-msg this-dir)
1472 this-file))
1473 (setq real-name this-real-name)))
1474 (setq file-count (1- file-count)))
1475 (setq base-dirs-count (1- base-dirs-count)))
1476 real-name))
1477
1478(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs)
1479 (let* ((real-name nil))
1480 (if (file-name-absolute-p file-name-from-err-msg)
1481 (setq real-name file-name-from-err-msg)
1482 (let* ((base-dirs-count (length base-dirs)))
1483 (while (and (not real-name) (> base-dirs-count 0))
1484 (let* ((full-name (expand-file-name file-name-from-err-msg
1485 (nth (1- base-dirs-count) base-dirs))))
1486 (if (file-exists-p full-name)
1487 (setq real-name full-name))
1488 (setq base-dirs-count (1- base-dirs-count))))))
1489 real-name))
1490
1491(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name)
1492 "Find buildfile, store its dir in buffer data and return its dir, if found."
1493 (let* ((buildfile-dir
1494 (flymake-find-buildfile buildfile-name
1495 (file-name-directory source-file-name))))
1496 (if buildfile-dir
1497 (setq flymake-base-dir buildfile-dir)
1498 (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name)
1499 (flymake-report-fatal-status
1500 "NOMK" (format "No buildfile (%s) found for %s"
1501 buildfile-name source-file-name)))))
1502
1503(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp)
1504 "Find master file (or buffer), create its copy along with a copy of the source file."
1505 (let* ((source-file-name buffer-file-name)
1506 (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))
1507 (master-and-temp-master (flymake-create-master-file
1508 source-file-name temp-source-file-name
1509 get-incl-dirs-f create-temp-f
1510 master-file-masks include-regexp)))
1511
1512 (if (not master-and-temp-master)
1513 (progn
1514 (flymake-log 1 "cannot find master file for %s" source-file-name)
1515 (flymake-report-status "!" "") ; NOMASTER
1516 nil)
1517 (setq flymake-master-file-name (nth 0 master-and-temp-master))
1518 (setq flymake-temp-master-file-name (nth 1 master-and-temp-master)))))
1519
1520(defun flymake-master-cleanup ()
1521 (flymake-simple-cleanup)
1522 (flymake-safe-delete-file flymake-temp-master-file-name))
1523
1524;;;; make-specific init-cleanup routines
1525(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f)
1526 "Create a command line for syntax check using GET-CMD-LINE-F."
1527 (funcall get-cmd-line-f
1528 (if use-relative-source
1529 (file-relative-name source-file-name base-dir)
1530 source-file-name)
1531 (if use-relative-base-dir
1532 (file-relative-name base-dir
1533 (file-name-directory source-file-name))
1534 base-dir)))
1535
1536(defun flymake-get-make-cmdline (source base-dir)
1537 (list "make"
1538 (list "-s"
1539 "-C"
1540 base-dir
1541 (concat "CHK_SOURCES=" source)
1542 "SYNTAX_CHECK_MODE=1"
1543 "check-syntax")))
1544
1545(defun flymake-get-ant-cmdline (source base-dir)
1546 (list "ant"
1547 (list "-buildfile"
1548 (concat base-dir "/" "build.xml")
1549 (concat "-DCHK_SOURCES=" source)
1550 "check-syntax")))
1551
1552(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f)
1553 "Create syntax check command line for a directly checked source file.
1554Use CREATE-TEMP-F for creating temp copy."
1555 (let* ((args nil)
1556 (source-file-name buffer-file-name)
1557 (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name)))
1558 (if buildfile-dir
1559 (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)))
1560 (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir
1561 use-relative-base-dir use-relative-source
1562 get-cmdline-f))))
1563 args))
1564
1565(defun flymake-simple-make-init ()
1566 (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline))
1567
1568(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp)
1569 "Create make command line for a source file checked via master file compilation."
1570 (let* ((make-args nil)
1571 (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
1572 get-incl-dirs-f 'flymake-create-temp-inplace
1573 master-file-masks include-regexp)))
1574 (when temp-master-file-name
1575 (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile")))
1576 (if buildfile-dir
1577 (setq make-args (flymake-get-syntax-check-program-args
1578 temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline)))))
1579 make-args))
1580
1581(defun flymake-find-make-buildfile (source-dir)
1582 (flymake-find-buildfile "Makefile" source-dir))
1583
1584;;;; .h/make specific
1585(defun flymake-master-make-header-init ()
1586 (flymake-master-make-init
1587 'flymake-get-include-dirs
1588 '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'")
1589 "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\""))
1590
1591;;;; .java/make specific
1592(defun flymake-simple-make-java-init ()
1593 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline))
1594
1595(defun flymake-simple-ant-java-init ()
1596 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline))
1597
1598(defun flymake-simple-java-cleanup ()
1599 "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs."
1600 (flymake-safe-delete-file flymake-temp-source-file-name)
1601 (when flymake-temp-source-file-name
1602 (flymake-delete-temp-directory
1603 (file-name-directory flymake-temp-source-file-name))))
1604
1605;;;; perl-specific init-cleanup routines
1606(defun flymake-perl-init ()
1607 (let* ((temp-file (flymake-init-create-temp-buffer-copy
1608 'flymake-create-temp-inplace))
1609 (local-file (file-relative-name
1610 temp-file
1611 (file-name-directory buffer-file-name))))
1612 (list "perl" (list "-wc " local-file))))
1613
1614;;;; php-specific init-cleanup routines
1615(defun flymake-php-init ()
1616 (let* ((temp-file (flymake-init-create-temp-buffer-copy
1617 'flymake-create-temp-inplace))
1618 (local-file (file-relative-name
1619 temp-file
1620 (file-name-directory buffer-file-name))))
1621 (list "php" (list "-f" local-file "-l"))))
1622
1623;;;; tex-specific init-cleanup routines
1624(defun flymake-get-tex-args (file-name)
1625 ;;(list "latex" (list "-c-style-errors" file-name))
1626 (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name)))
1627
1628(defun flymake-simple-tex-init ()
1629 (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace)))
1630
1631;; Perhaps there should be a buffer-local variable flymake-master-file
1632;; that people can set to override this stuff. Could inherit from
1633;; the similar AUCTeX variable.
1634(defun flymake-master-tex-init ()
1635 (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
1636 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace
1637 '("\\.tex\\'")
1638 "[ \t]*\\in\\(?:put\\|clude\\)[ \t]*{\\(.*%s\\)}")))
1639 (when temp-master-file-name
1640 (flymake-get-tex-args temp-master-file-name))))
1641
1642(defun flymake-get-include-dirs-dot (_base-dir)
1643 '("."))
1644
1645;;;; xml-specific init-cleanup routines
1646(defun flymake-xml-init ()
1647 (list flymake-xml-program
1648 (list "val" (flymake-init-create-temp-buffer-copy
1649 'flymake-create-temp-inplace))))
39 1650
40(provide 'flymake) 1651(provide 'flymake)
41;;; flymake.el ends here 1652;;; flymake.el ends here